Movable barrier operator

ABSTRACT

A movable barrier operator having improved safety and energy efficiency features automatically detects line voltage frequency and uses that information to set a worklight shut-off time. The operator automatically detects the type of door (single panel or segmented) and uses that information to set a maximum speed of door travel. The operator moves the door with a linearly variable speed from start of travel to stop for smooth and quiet performance. The operator provides for full door closure by driving the door into the floor when the DOWN limit is reached and no auto-reverse condition has been detected. The operator provides for user selection of a minimum stop speed for easy starting and stopping of sticky or binding doors.

BACKGROUND OF THE INVENTION

[0001] This invention relates generally to movable barrier operators for operating movable barriers or doors. More particularly, it relates to garage door operators having improved safety and energy efficiency features.

[0002] Garage door operators have become more sophisticated over the years providing users with increased convenience and security. However, users continue to desire further improvements and new features such as increased energy efficiency, ease of installation, automatic configuration, and aesthetic features, such as quiet, smooth operation.

[0003] In some markets energy costs are significant. Thus energy efficiency options such as lower horsepower motors and user control over the worklight functions are important to garage door operator owners. For example, most garage door operators have a worklight which turns on when the operator is commanded to move the door and shuts off a fixed period of time after the door stops. In the United States, an illumination period of 4½ minutes is considered adequate. In markets outside the United States, 4½ minutes is considered too long. Some garage door operators have special safety features, for example, which enable the worklight whenever the obstacle detection beam is broken by an intruder passing through an open garage door. Some users may wish to disable the worklight in this situation. There is a need for a garage door operator which can be automatically configured for predefined energy saving features, such as worklight shut-off time.

[0004] Some movable barrier operators include a flasher module which causes a small light to flash or blink whenever the barrier is commanded to move. The flasher module provides some warning when the barrier is moving. There is a need for an improved flasher unit which provides even greater warning to the user when the barrier is commanded to move.

[0005] Another feature desired in many markets is a smooth, quiet motor and transmission. Most garage door operators have AC motors because they are less expensive than DC motors. However, AC motors are generally noisier than DC motors.

[0006] Most garage door operators employ only one or two speeds of travel. Single speed operation, i.e., the motor immediately ramps up to full operating speed, can create a jarring start to the door. Then during closing, when the door approaches the floor at full operating speed, whether a DC or AC motor is used, the door closes abruptly with a high amount of tension on it from the inertia of the system. This jarring is hard on the transmission and the door and is annoying to the user.

[0007] If two operating speeds are used, the motor would be started at a slow speed, usually 20 percent of full operating speed, then after a fixed period of time, the motor speed would increase to full operating speed. Similarly, when the door reaches a fixed point above/below the close/open limit, the operator would decrease the motor speed to 20 percent of the maximum operating speed. While this two speed operation may eliminate some of the hard starts and stops, the speed changes can be noisy and do not occur smoothly, causing stress on the transmission. There is a need for a garage door operator which opens the door smoothly and quietly, with no aburptly apparent sign of speed change during operation.

[0008] Garage doors come in many types and sizes and thus different travel speeds are required for them. For example, a one-piece door will be movable through a shorter total travel distance and need to travel slower for safety reasons than a segmented door with a longer total travel distance. To accommodate the two door types, many garage door operators include two sprockets for driving the transmission. At installation, the installer must determine what type of door is to be driven, then select the appropriate sprocket to attach to the transmission. This takes additional time and if the installer is the user, may require several attempts before matching the correct sprocket for the door. There is a need for a garage door operator which automatically configures travel speed depending on size and weight of the door.

[0009] National safety standards dictate that a garage door operator perform a safety reversal (auto-reverse) when an object is detected only one inch above the DOWN limit or floor. To satisfy these safety requirements, most garage door operators include an obstacle detection system, located near the bottom of the door travel. This prevents the door from closing on objects or persons that may be in the door path. Such obstacle detection systems often include an infrared source and detector located on opposite sides of the door frame. The obstacle detector sends a signal when the infrared beam between the source and detector is broker,, indicating an obstacle is detected. In response to the obstacle signal, the operator causes an automatic safety reversal. The door stops and begins traveling up, away from the obstacle.

[0010] There are two different “forces” used in the operation of the garage door operator. The first “force” is usually preset or setable at two force levels: the UP force level setting used to determine the speed at which the door travels in the UP direction and the DOWN force level setting used to determine the speed at which the door travels in the DOWN direction. The second “force” is the force level determined by the decrease in motor speed due to an external force applied to the door, i.e., from an obstacle or the floor. This external force level is also preset or setable and is any set-point type force against which the feedback force signal is compared. When the system determines the set point force has been met, an auto-reverse or stop is commanded.

[0011] To overcome differences in door installations, i.e. stickiness and resistance to movement and other varying frictional-type forces, some garage door operators permit the maximum force (the second force) used to drive the speed of travel to be varied manually. This, however, affects the system's auto-reverse operation based on force. The auto-reverse system based on force initiates an auto-reverse if the force on the door exceeds the maximum force setting (the second force) by some predetermined amount. If the user increases the force setting to drive the door through a “sticky” section of travel, the user may inadvertently affect the force to a much greater value than is safe for the unit to operate during normal use. For example, if the DOWN force setting is set so high that it is only a small incremental value less than the force setting which initiates an auto-reverse due to force, this causes the door to engage objects at a higher speed before reaching the auto-reverse force setting. While the obstacle detection system will cause the door to auto-reverse, the speed and force at which the door hits the obstacle may cause harm to the obstacle and/or the door.

[0012] Barrier movement operators should perform a safety reversal off an obstruction which is only marginally higher than the floor, yet still close the door safely against the floor. In operator systems where the door moves at a high speed, the relatively large momentum of the moving parts, including the door, accomplishes complete closure. In systems with a soft closure, where the door speed decreases from full maximum to a small percentage of full maximum when closing, there may be insufficient momentum in the door or system to accomplish a full closure. For example, even if the door is positioned at the floor, there is sometimes sufficient play in the trolley of the operator to allow the door to move if the user were to try to open it. In particular, in systems employing a DC motor, when the DC motor is shut off, it becomes a dynamic brake. If the door isn't quite at the floor when the DOWN travel limit is reached and the DC motor is shut off, the door and associated moving parts may not have sufficient momentum to overcome the braking force of the DC motor. There is a need for a garage door operator which closes the door completely, eliminating play in the door after closure.

[0013] Many garage door operator installations are made to existing garage doors. The amount of force needed to drive the door varies depending on type of door and the quality of the door frame and installation. As a result, some doors are “stickier” than others, requiring greater force to move them through the entire length of travel. If the door is started and stopped using the full operating speed, stickiness is not usually a problem. However, if the garage door operator is capable of operation at two speeds, stickiness becomes a larger problem at the lower speed. In some installations, a force sufficient to run at 20 percent of normal speed is too small to start some doors moving. There is a need for a garage door operator which automatically controls force output and thus start and stop speeds.

SUMMARY OF THE INVENTION

[0014] A movable barrier operator having an electric motor for driving a garage door, a gate or other barrier is operated from a source of AC current. The movable barrier operator includes circuitry for automatically detecting the incoming AC line voltage and frequency of the alternating current. By automatically detecting the incoming AC line voltage and determining the frequency, the operator can automatically configure itself to certain user preferences. This occurs without either the user or the installer having to adjust or program the operator. The movable barrier operator includes a worklight for illuminating its immediate surroundings such as the interior of a garage. The barrier operator senses the power line frequency (typically 50 Hz or 60 Hz) to automatically set an appropriate shut-off time for a worklight. Because the power line frequency in Europe is 50 Hz and in the U.S. is 60 Hz, sensing the power line frequency enables the operator to configure itself for either a European or a U.S. market with no user or installer modifications. For U.S. users, the worklight shut-off time is set to preferably 4½ minutes; for European users, the worklight shut-off time is set to preferably 2½ minutes. Thus, a single barrier movement operator can be sold in two different markets with automatic setup, saving installation time.

[0015] The movable barrier operator of the present invention automatically detects if an optional flasher module is present. If the module is present, when the door is commanded to move, the operator causes the flasher module to operate. With the flasher module present, the operator also delays operation of the motor for a brief period, say ore or two seconds. This delay period with the flasher module blinking before door movement provides an added safety feature to users which warns them of impending door travel (e.g. if activated by an unseen transmitter).

[0016] The movable barrier operator of the present invention drives the barrier, which may be a door or a gate, at a variable speed. After motor start, the electric motor reaches a preferred initial speed of 20 percent of the full operating speed. The motor speed then increases slowly in a linearly continuous fashion from 20 percent to 100 percent of full operating speed. This provides a smooth, soft start without jarring the transmission or the door or gate. The motor moves the barrier at maximum speed for the largest portion of its travel, after which the operator slowly decreases speed from 100 percent to 20 percent as the barrier approaches the limit of travel, providing a soft, smooth and quiet stop. A slow, smooth start and stop provides a safer barrier movement operator for the user because there is less momentum to apply an impulse force in the event of an obstruction. In a fast system, relatively high momentum of the door changes to zero at the obstruction before the system can actually detect the obstruction. This leads to the application of a high impulse force. With the system of the invention, a slower stop speed means the system has less momentum to overcome, and therefore a softer, more forgiving force reversal. A slow, smooth start and stop also provide a more aesthetically pleasing effect to the user, and when coupled with a quieter DC motor, a barrier movement operator which operates very quietly.

[0017] The operator includes two relays and a pair of field effect transistors (FETs) for controlling the motor. The relays are used to control direction of travel. The FET's, with phase controlled, pulse width modulation, control start up and speed. Speed is responsive to the duration of the pulses applied to the FETs. A longer pulse causes the FETs to be on longer causing the barrier speed to increase. Shorter pulses result in a slower speed. This provides a very fine ramp control and more gentle starts and stops.

[0018] The movable barrier operator provides for the automatic measurement and calculation of the total distance the door is to travel. The total door travel distance is the distance between the UP and the DOWN limits (which depend on the type of door). The automatic measurement of door travel distance is a measure of the length of the door. Since shorter doors must travel at slower speeds than normal doors (for safety reasons), this enables the operator to automatically adjust the motor speed so the speed of door travel is the same regardless of door size. The total door travel distance in turn determines the maximum speed at which the operator will travel. By determining the total distance traveled, travel speeds can be automatically changed without having to modify the hardware.

[0019] The movable barrier operator provides full door or gate closure, i.e. a firm closure of the door to the floor so that the door is not movable in place after it stops. The operator includes a digital control or processor, specifically a microcontroller which has an internal microprocessor, an internal RAM and an internal ROM and an external EEPROM. The microcontroller executes instructions stored in its internal ROM and provides motor direction control signals to the relays and speed control signals to the FETs. The operator is first operated in a learn mode to store a DOWN limit position for the door. The DOWN limit position of the door is used as an approximation of the location of the floor (or as a minimum reversal point, below which no auto-reverse will occur). When the door reaches the DOWN limit position, the microcontroller causes the electric motor to drive the door past the DOWN limit a small distance, say for one or two inches. This causes the door to close solidly on the floor.

[0020] The operator embodying the present invention provides variable door or gate output speed, i.e., the user can vary the minimum speed at which the motor starts and stops the door. This enables the user to overcome differences in door installations, i.e. stickiness and resistance to movement and other varying functional-type forces. The minimum barrier speeds in the UP and DOWN directions are determined by the user-configured force settings, which are adjusted using UP and DOWN force potentiometers. The force potentiometers set the lengths of the pulses to the FETs, which translate to variable speeds. The user gains a greater force output and a higher minimum starting speed to overcome differences in door installations, i.e. stickiness and resistance to movement and other varying functional-type forces speed, without affecting the maximum speed of travel for the door. The user can configure the door to start at a speed greater than a default value, say 20 percent. This greater start up and slow down speed is transferred to the linearly variable speed function in that instead of traveling at 20 percent speed, increasing to 100 percent speed, then decreasing to 20 percent speed, the door may, for instance, travel at 40 percent speed to 100 percent speed and back down to 40 percent speed.

BRIEF DESCRIPTION OF THE DRAWINGS

[0021]FIG. 1 is a perspective view of a garage having mounted within it a garage door operator embodying the present invention;

[0022]FIG. 2 is an exploded perspective view of a head unit of the garage door operator shown in FIG. 1;

[0023]FIG. 3 is an, exploded perspective view of a portion of a transmission unit of the garage door operator shown in FIG. 1;

[0024]FIG. 4 is a block diagram of a controller and motor mounted within the head unit of the garage door operator shown in FIG. 1;

[0025] FIGS. 5A-5D are a schematic diagram of the controller shown in block format in FIG. 4;

[0026] FIGS. 6A-6B are a flow chart of an overall routine that executes in a microprocessor of the controller shown in FIGS. 5A-5D;

[0027] FIGS. 7A-7H are a flow chart of the main routine executed in the microprocessor;

[0028]FIG. 8 is a flow chart of a set variable light shut-off timer routine executed by the microprocessor;

[0029] FIGS. 9A-9C are a flow chart of a hardware timer interrupt routine executed in the microprocessor;

[0030] FIGS. 10A-10C are a flow chart of a 1 millisecond timer routine executed in the microprocessor;

[0031] FIGS. 11A-11C are a flow chart of a 125 millisecond timer routine executed in the microprocessor;

[0032] FIGS. 12A-12B are a flow chart of a 4 millisecond timer routine executed in the microprocessor;

[0033] FIGS. 13A-13B are a flow chart of an RPM interrupt routine executed in the microprocessor;

[0034]FIG. 14 is a flow chart of a motor state machine routine executed in the microprocessor;

[0035]FIG. 15 is a flow chart of a stop in midtravel routine executed in the microprocessor;

[0036]FIG. 16 is a flow chart of a DOWN position routine executed in the microprocessor;

[0037] FIGS. 17A-17C are a flow chart of an UP direction routine executed in the microprocessor;

[0038]FIG. 18 is a flow chart of an auto-reverse routine executed in the microprocessor;

[0039]FIG. 19 is a flow chart of an UP position routine executed in the microprocessor;

[0040] FIGS. 20A-20D are a flow chart of the DOWN direction routine executed in the microprocessor;

[0041]FIG. 21 is an exploded perspective view of a pass point detector and motor of the operator shown in FIG. 2;

[0042]FIG. 22A is a plan view of the pass point detector shown in FIG. 21; and

[0043]FIG. 22B is a partial plan view of the pass point detector shown in FIG. 21.

DETAILED DESCRIPTION OF THE PREFERRED EMBODIMENT

[0044] Referring now to the drawings and especially to FIG. 1, a movable barrier or garage door operator system is generally shown therein and referred to by numeral 8. The system 8 includes a movable barrier operator or garage door operator 10 having a head unit 12 mounted within a garage 14. More specifically, the head unit 12 is mounted to a ceiling 15 of the garage 14. The operator 10 includes a transmission 18 extending from the head unit 12 with a releasable trolley 20 attached. The releasable trolley 20 releasably connects an arm 22 extending to a single panel garage door 24 positioned for movement along a pair of door rails 26 and 28.

[0045] The system 8 includes a hand-held RF transmitter unit 30 adapted to send signals to an antenna 32 (see FIG. 4) positioned on the head unit 12 and coupled to a receiver within the head unit 12 as will appear hereinafter. A switch module 39 is mounted on the head unit 12. Switch module 39 includes switches for each of the commands available from a remote transmitter or from an optional wall-mounted switch (not shown). Switch module 39 enables an installer to conveniently request the various learn modes during installation of the head unit 12. The switch module 39 includes a learn switch, a light switch, a lock switch and a command switch, which are described below. Switch module 39 may also include terminals for wiring a pedestrian door state sensor comprising a pair of contacts 13 and 15 for a pedestrian door 11, as well as wiring for an optional wall switch (not shown).

[0046] The garage door 24 includes the pedestrian door 11. Contact 13 is mounted to door 24 for contact with contact 15 mounted to pedestrian door 11. Both contacts 13 and 15 are connected via a wire 17 to head unit 12. As will be described further below, when the pedestrian door 11 is closed, electrical contact is made between the contacts 13 and 15 closing a pedestrian door circuit in the receiver in head unit 12 and signalling that the pedestrian door state is closed. This circuit must be closed before the receiver will permit other portions of the operator to move the door 24. If circuit is open, indicating that the pedestrian door state is open, the system will not permit door 24 to move.

[0047] The head unit 12 includes a housing comprising four sections: a bottom section 102, a front section 106, a back section 108 and a top section 110, which are held together by screws 112 as shown in FIG. 2. Cover 104 fits into front section 106 and provides a cover for a worklight. External AC power is supplied to the operator 10 through a power cord 112. The AC power is applied to a step-down transformer 120. An electric motor 118 is selectively energized by rectified AC power and drives a sprocket 125 in sprocket assembly 124. The sprocket 125 drives chain 144 (see FIG. 3). A printed circuit board 114 includes a controller 200 and other electronics for operating the head unit 12. A cable 116 provides input and output connections or, signal paths between the printed circuit board 114 and switch module 39. The transmission 18, as shown in FIG. 3, includes a rail 142 which holds chain 144 within a rail and chain housing 140 and holds the chair in tension to transfer mechanical energy from the motor to the door.

[0048] A block diagram or the controller and motor connections is shown in FIG. 4. Controller 200 includes an RF receiver 80, a microprocessor 300 and an EEPROM 302. RF receiver 80 of controller 200 receives a command to move the door and actuate the motor either from remote transmitter 30, which transmits an RF signal which is received by antenna 32, or from a user command switch 250. User command switch 250 can be a switch on switch panel 39, mounted on the head unit, or a switch from an optional wall switch. Upon receipt of a door movement command signal from either antenna 32 or user switch 250, the controller 200 sends a power enable signal via line 240 to AC hot connection 206 which provides AC line current to transformer 212 and power to work light 210. Rectified AC is provided from rectifier 214 via line 236 to relays 232 and 234. Depending on the commanded direction of travel, controller 200 provides a signal to either relay 232 or relay 234. Relays 232 and 234 are used to control the direction of rotation of motor 118 by controlling the direction of current flow through the windings. One relay is used for clockwise rotation; the other is used for counterclockwise rotation.

[0049] Upon receipt of the door movement command signal, controller 200 sends a signal via line 230 to power-control FET 252. Motor speed is determined by the duration or length of the pulses in the signal to a gate electrode of FET 252. The shorter the pulses, the slower the speed. This completes the circuit between relay 232 and FET 252 providing power to motor 118 via line 254. If the door had been commanded to move in the opposite direction, relay 234 would have been enabled, completing the circuit with FET 252 and providing power to motor 118 via line 238.

[0050] With power provided, the motor 118 drives the output shaft 216 which provides drive power to transmission sprocket 125. Gear redaction housing 260 includes an internal pass point system which sends a pass point signal via line 220 to controller 220 whenever the pass point is reached. The pass point signal is provided to controller 200 via current limiting resistor 226 to protect controller 200 from electrostatic discharge (ESD). An RPM interrupt signal is provided via line 224, via current limiting resistor 228, to controller 200. Lead 222 provides a plus five volts supply for the Hall effect sensors in the RPM module. Commanded force is input by two force potentiometers 202, 204. Force potentiometer 202 is used to set the commanded force for UP travel; force potentiometer 204 is used to set the commanded force for DOWN travel. Force potentiometers 202 and 204 provide commanded inputs to controller 200 which are used to adjust the length of the pulsed signal provided to FET 252.

[0051] The pass point for this system is provided internally in the motor 118. Referring to FIG. 22, the pass point module 40 is attached to gear reduction housing 260 of motor 118. Pass point module 40 includes upper plate 42 which covers the three internal gears and switch within lower housing 50. Lower housing 50 includes recess 62 having two pins 61 which position switch assembly 52 in recess 62. Housing 50 also includes three cutouts which are sized to support and provide for rotation of the three geared elements. Outer gear 44 fits rotatably within cutout 64. Outer gear includes a smooth outer surface for rotating within housing 50 and inner gear teeth for rotating middle gear 46. Middle gear 46 fits rotatably within inner cutout 66. Middle gear 46 includes a smooth outer surface and a raised portion with gear teeth for being driven by the gear teeth of outer ring gear 44. Inner gear 48 fits within middle gear 46 and is driven by an extension of shaft 216. Rotation of the motor 118 causes shaft 216 to rotate and drive inner gear 48.

[0052] Outer gear 44 includes a notch 74 in the outer periphery. Middle gear includes a notch 76 in the outer periphery. Referring to FIG. 22A, rotation of inner gear 48 rotates middle gear 46 in the same direction. Rotation of middle gear 46 rotates outer gear 44 in the same direction. Gears 46 and 44 are sized such that pass point indications comprising switch release cutouts 74 and 76 line up only once during the entire travel distance of the door. As seen in FIG. 22A, when switch release cutouts 74 and 76 line up, switch 72 is open generating a pass point presence signal. The location where switch release cutouts 74 and 76 line up is the pass point. At all other times, at least one of the two gears holds switch 72 closed generating a signal indicating that the pass point has not been reached.

[0053] The receiver portion 80 of controller 200 is shown in FIG. 5A. RF signals may be received by the controller 200 at the antenna 32 and fed to the receiver 80. The receiver 80 includes variable inductor L1 and a pair of capacitors C2 and C3 that provide impedance matching between the antenna 32 and other portions of the receiver. An NPN transistor Q4 is connected in common-base configuration as a buffer amplifier. Bias to the buffer amplifier transistor Q4 is provided by resistors R2, R3. The buffered RF output signal is supplied to a second NPN transistor Q5. The radio frequency signal is coupled to a bandpass amplifier 280 to an average detector 282 which feeds a comparator 284. Referring to FIGS. 5C and 5B, the analog output signal A, B is applied to noise reduction capacitors C19, C20 and C21 then provided to pins P32 and P33 of the microcontroller 300. Microcontroller 300 may be a Z86733 microprocessor.

[0054] An external transformer 212 receives AC power from a source such as a utility and steps down the AC voltage to the power supply 90 circuit of controller 200. Transformer 212 provides AC current to full-wave bridge circuit 214, which produces a 28 volt full wave rectified signal across capacitor C35. The AC power may have a frequency of 50 Hz or 60 Hz. An external transformer is especially important when motor 118 is a DC motor. The 28 volt rectified signal is used to drive a wall control switch, a obstacle detector circuit, a door-in-door switch and to power FETs Q11 and Q12 used to start the motor. Zener diode D18 protects against overvoltage due to the pulsed current, in particular, from the FETs rapidly switching off inductive load of the motor. The potential of the full-wave rectified signal is further reduced to provide 5 volts at capacitor C38, which is used to power the microprocessor 300, the receiver circuit 80 and other logic functions.

[0055] The 28 volt rectified power supply signal indicated by reference numeral T in FIG. 5C is voltage divided down by resistors R61 and R62, then applied to an input pin P24 of microprocessor 300. This signal is used to provide the phase of the power line current to microprocessor 300. Microprocessor 300 constantly checks for the phase of the line voltage in order to determine if the frequency of the line voltage is 50 Hz or 60 Hz. This information is used to establish the worklight time-out period and to select the look-up table stored in the ROM in the microcontroller for converting pulse width to door speed.

[0056] When the door is commanded to move, either through a signal from a remote transmitter received through antenna 32 and processed by receiver 80, or through an optional wall switch, the microprocessor 300 commands the work light to turn on. Microprocessor 300 sends a worklight enable signal from pin P07. The worklight enable signal is applied to the base of transistor Q3, which drives relay K3. AC power from a signal U provides power for operating the worklight 210.

[0057] Microprocessor 300 reads from and writes data to an EEPROM 302 via its pins P25, P26 and P27. EEPROM 302 may be a 93C46. Microprocessor 300 provides a light enable signal at pin P21 which is used to enable a learn mode indicator yellow LED D15. LED D15 is enabled or lit when the receiver is in the learn mode. Pin P26 provides double duty. When the user selects switch S1, a learn enable signal is provided to both microprocessor 300 and EEPROM 302. Switch S1 is mounted on the head unit 12 and is part of switch module 39, which is used by the installer to operate the system.

[0058] An optional flasher module provides an additional level of safety for users and is controlled by microprocessor 300 at pin P22. The optional flasher module is connected between terminals 308 and 310. In the optional flasher module, after receipt of a door command, the microprocessor 300 sends a signal from P22 which causes the flasher light to blink for 2 seconds. The door does not move during that 2 second period, giving the user notice that the door has been commanded to move and will start to move in 2 seconds. After expiration of the 2 second period, the door moves and the flasher light module blinks during the entire period of door movement. If the operator does not have a flasher module installed in the head unit, when the door is commanded to move, there is no time delay before the door begins to move.

[0059] Microprocessor 300 provides the signals which start motor 116, control its direction of rotation (and thus the direction of movement of the door) and the speed of rotation (speed of door travel). FETs Q11 and Q12 are used to start motor 118. Microprocessor 300 applies a pulsed output signal to the gates of FETs Q11 and Q12. The lengths of the pulses determine the time the FETs conduct and thus the amount of time current is applied to start and run the motor 118. The longer the pulse, the longer current is applied, the greater the speed of rotation the motor 118 will develop. Diode D11 is coupled between the 28 volt power supply and is used to clean up flyback voltage to the input bridge D4 when the FETs are conducting. Similarly, Zener diode D19 (see FIG. 5A) is used to protect against overvoltage when the FETs are conducting.

[0060] Control of the direction of rotation of motor 118 (and thus direction of travel of the door) is accomplished with two relays, K1 and K2. Relay K1 supplies current to cause the motor to rotate clockwise in an opening direction (door moves UP); relay K2 supplies current to cause the motor to rotate counterclockwise in a closing direction (door moves DOWN). When the door is commanded to move UP, the microprocessor 300 sends an enable signal from pin P05 to the base of transistor Q1, which drives relay K1. When the door is commanded to move DOWN, the microprocessor 300 sends an enable signal from pin P06 to the base of transistor Q2, which drives relay K2.

[0061] Door-in-door contacts 13 and 15 are connected to terminals 304 and 306. Terminals 304 and 306 are connected to relays K1 and K2. If the signal between contacts 13 and 15 is broken, the signal across terminals 304 and 306 is open, preventing relays K1 and K2 from energizing. The motor 118 will not rotate and the door 24 will not move until the user closes pedestrian door 11, making contact between contacts 13 and 15.

[0062] The pass point signal 220 from the pass point module 40 (see FIG. 21) of motor 118 is applied to pin P23 of microprocessor 300. The RPM signal 224 from the RPM sensor module in motor 118 is applied to pin P31 of microprocessor 300. Application of the pass point signal and the RPM signal is described with reference to the flow charts.

[0063] An optional wall control, which duplicates the switches on remote transmitter 30, may be connected to controller 200 at terminals 312 and 314. When the user presses the door command switch 39, a dead short is made to ground, which the microprocessor 300 detects by the failure to detect voltage. Capacitor C22 is provided for RF noise reduction. The dead short to ground is sensed at pins P02 and P03, for redundancy.

[0064] Switches S1 and S2 are part of switch module 39 mounted on head unit 12 and used by the installer for operating the system. As stated above, S1 is the learn switch. S2 is the door command switch. When S2 is pressed, microprocessor 300 detects the dead short at pins P02 and P03.

[0065] Input from an obstacle detector (not shown) is provided at terminal 316. This signal is voltage divided down and provided to microprocessor 300 at pins P20 and P30, for redundancy. Except when the door is moving and less than an inch above the floor, when the obstacle detector senses an object in the doorway, the microprocessor executes the auto-reverse routine causing the door to stop and/or reverse depending on the state of the door movement.

[0066] Force and speed of door travel are determined by two potentiometers. Potentiometer R33 adjusts the force and speed of UP travel; potentiometer R34 adjusts the force and speed of DOWN travel. Potentiometers R33 and R34 act as analog voltage dividers. The analog signal from R33, R34 is further divided down by voltage divider R35/R37, R36/R38 before it is applied to the input of comparators 320 and 322. Reference pulses from pins P34 and P35 of microprocessor 300 are compared with the force input from potentiometers R33 and R34 in comparators 320 and 322. The output of comparators 320 and 322 is applied to pins P01 and P00.

[0067] To perform the A/D conversion, the microprocessor 300 samples the output of the comparators 320 and 322 at pins P00 and P01 to determine which voltage is higher: the voltage from the potentiometer R33 or R34 (IN) or the voltage from the reference pin P34 or P35 (REF). If the potentiometer voltage is higher than the reference, then the microprocessor outputs a pulse. If not, the output voltage is held low. The RC filter (R39, C29/R40, C30) converts the pulses into a DC voltage equivalent to the duty cycle of the pulses. By outputting the pulses in the manner described above, the microprocessor creates a voltage at REF which dithers around the voltage at IN. The microprocessor then calculates the duty cycle of the pulse output which directly correlates to the voltage seen at IN.

[0068] When power is applied to the head unit 12 including controller 200, microprocessor 300 executes a series of routines. With power applied, microprocessor 300 executes the main routines shown in FIGS. 6A and 6B. The main loop 400 includes three basic functions, which are looped continuously until power is removed. In block 402 the microprocessor 300 handles all non-radio EEPROM communications and disables radio access to the EEPROM 302 when communicating. This ensures that during normal operation, i.e., when the garage door operator is not being programmed, the remote transmitter does not have access to the EEPROM, where transmitter codes are stored. Radio transmissions are processed upon receipt of a radio interrupt (see below).

[0069] In block 404, microprocessor 300 maintains all low priority tasks, such as calculating new force levels and minimum speed. Preferably, a set of redundant RAM registers is provided. In the event of an unforeseen event (e.g., an ESD event) which corrupts regular RAM, the main RAM registers and the redundant RAM registers will not match. Thus, when the values in RAM do not match, the routine knows the regular RAM has been corrupted. (See block 504 below.) In block 406, microprocessor 300 tests redundant RAM registers. Several interrupt routines can take priority over blocks 402, 404 and 406.

[0070] The infrared obstacle detector generates an asynchronous IR interrupt signal which is a series of pulses. The absence of the obstacle detector pulses indicates an obstruction in the beam. After processing the IR interrupt, microprocessor 300 sets the status of the obstacle detector as unobstructed at block 416.

[0071] Receipt of a transmission from remote transmitter 30 generates an asynchronous radio interrupt at block 410. At block 418, if in the door command mode, microprocessor 300 parses incoming radio signals and sets a flag if the signal matches a stored code. If in the learn mode, microprocessor 300 stores the new transmitter codes in the EEPROM.

[0072] An asynchronous interrupt is generated if a remote communications unit is connected to an optional RS-232 communications port located on the head unit. Upon receipt of the hardware interrupt, microprocessor 300 executes a serial data communications routine for transferring and storing data from the remote hardware.

[0073] Hardware timer 0 interrupt is shown in block 422. In block 422, microprocessor 300 reads the incoming AC line signal from pin P24 and handles the motor phase control output. The incoming line signal is used to determine if the line voltage is 50 Hz for the foreign market or 60 Hz for the domestic market. With each interrupt, microprocessor 300, at block 426, task switches among three tasks. In block 428, microprocessor 300 updates software timers. In block 430, microprocessor 300 debounces wall control switch signals. In block 432, microprocessor 300 controls the motor state, including motor direction relay outputs and motor safety systems.

[0074] When the motor 118 is running, it generates an asynchronous RPM interrupt at block 434. When microprocessor 300 receives the asynchronous RPM interrupt at pin P31, it calculates the motor RPM period at block 436, then updates the position of the door at block 438.

[0075] Further details of main loop 400 are shown in FIGS. 7A through 7H. The first step executed in main loop 400 is block 450, where the microprocessor checks to see if the pass point has been passed since the last update. If it has, the routine branches to block 452, where the microprocessor 300 updates the position of the door relative to the pass point in EEPROM 302 or non-volatile memory. The routine then continues at block 454. An optional safety feature of the garage door operator system enables the worklight, when the door is open and stopped and the infrared beam in the obstacle detector is broken.

[0076] At block 454, the microprocessor checks if the enable/disable of the worklight for this feature has been changed. Some users want the added safety feature; others prefer to save the electricity used. If new input has been provided, the routine branches to block 456 and sets the status of the obstacle detector-controlled worklight in non-volatile memory in accordance with the new input. Then the routine continues to block 458 where the routine checks to determine if the worklight has been turned on without the timer. A separate switch is provided on both the remote transmitter 30 and the head unit at module 39 to enable the user to switch on the worklight without operating the door command switch. If no, the routine skips to block 470.

[0077] If yes, the routine checks at block 460 to see if the one-shot flag has been set for an obstacle detector beam break. If no, the routine skips to block 470. If yes, the routine checks if the obstacle detector controlled worklight is enabled at block 462. If not, the routine skips to block 470. If it is, the routine checks if the door is stopped in the fully open position at block 464. If no, the routine skips to block 470. If yes, the routine calls the SetVarLight subroutine (see FIG. 8) to enable the appropriate turn off time (4.5 minutes for 60 Hz systems or 2.5 minutes for 50 Hz systems). At block 468, the routine turns on the worklight.

[0078] At block 470, the microprocessor 300 clears the one-shot flag for the infrared beam break. This resets the obstacle detector, so that a later beam break can generate an interrupt. At block 472, if the user has installed a temporary password usable for a fixed period of time, the microprocessor 300 updates the non-volatile timer for the radio temporary password. At block 474, the microprocessor 300 refreshes the RAM registers for radio mode from non-volatile memory (EEPROM 302). At block 476, the microprocessor 300 refreshes I/O port directions, i.e., whether each of the ports is to be input or output. At block 478, the microprocessor 300 updates the status of the radio lockout flag, if necessary. The radio lockout flag prevents the microprocessor from responding to a signal from a remote transmitter. A radio interrupt (described below) will disable the radio lockout flag and enable the remote transmitter to communicate with the receiver.

[0079] At block 480, the microprocessor 300 checks if the door is about to travel. If not, the routine skips to block 502. If the door is about to travel, the microprocessor 300 checks if the limits are being trained at block 482. If they are, the routine skips to block 502. If not, the routine asks at block 484 if travel is UP or DOWN. If DOWN, the routine refreshes the DOWN limit from non-volatile memory (EEPROM 302) at block 486. If UP, the routine refreshes the UP limit from non-volatile memory (EEPROM 302) at block 488. The routine updates the current operating state and position relative to the pass point in non-volatile memory at block 490. This is a redundant read for stability of the system.

[0080] At block 492, the routine checks for completion of a limit training cycle. If training is complete, the routine branches to block 494 where the new limit settings and position relative to the pass point are written to non-volatile memory.

[0081] The routine then updates the counter for the number of operating cycles at block 496. This information can be downloaded at a later time and used to determine when certain parts need to be replaced. At block 498 the routine checks if the number of cycles is a multiple of 256. Limiting the storage of this information to multiples of 256 limits the number of times the system has to write to that register. If yes it updates the history of force settings at block 500. If not, the routine continues to block 502.

[0082] At block 502 the routine updates the learn switch debouncer. At block 504 the routine performs a continuity check by comparing the backup (redundant) RAM registers with the main registers. If they do not match, the routine branches to block 506. If the registers do not match, the RAM memory has been corrupted and the system is not safe to operate, so a reset is commanded. At this point, the system powers up as if power had been removed and reapplied and the first step is a self test of the system (all installation settings are unchanged).

[0083] If the answer to block 504 is yes, the routine continues to block 508 where the routine services any incoming serial messages from the optional wall control (serial messages might be user input start or stop commands). The routine then loads the UP force timing from the ROM look-up table, using the user setting as an index at block 510. Force potentiometers R33 and R34 are set by the user. The analog values set by the user are converted to digital values. The digital values are used as an index to the look-up table stored in memory. The value indexed from the look-up table is then used as the minimum motor speed measurement. When the motor runs, the routine compares the selected value from the look-up table with the digital timing from the RPM routine to ensure the force is acceptable.

[0084] Instead of calculating the force each time the force potentiometers are set, a look-up table is provided for each potentiometer. The range of values based on the range of user inputs is stored in ROM and used to save microprocessor processing time. The system includes two force limits: one for the UP force and one for the DOWN force. Two force limits provide a safer system. A heavy door may require more UP force to lift, but need a lower DOWN force setting (and therefore a slower closing speed) to provide a soft closure. A light door will need less UP force to open the door and possibly a greater DOWN force to provide a full closure.

[0085] Next the force timing is divided by power level of the motor for the door to scale the maximum force timeout at block 512. This step scales the force reversal point based on the maximum force for the door. The maximum force for the door is determined based on the size of the door, i.e. the distance the door travels. Single piece doors travel a greater distance than segmented doors. Short doors require less force to move than normal doors. The maximum force for a short door is scaled down to 60 percent of the maximum force available for a normal door. So, at block 512, if the force setting is set by the user, for example at 40 percent, and the door is a normal door (i.e., a segmented door or multi-paneled door), the force is scaled to 40 percent of 100 percent. If the door is a short door (i.e., a single panel door), the force is scaled to 40 percent of 60 percent, or 24 percent.

[0086] At block 514, the routine loads the DOWN force timing from the ROM look-up table, using the user setting as an index. At block 516, the routine divides the force timing by the power level of the motor for the door to scale the force to the speed.

[0087] At block 518 the routine checks if the door is traveling DOWN. If yes, the routine disables use of the MinSpeed Register at block 524 and loads the MinSpeed Register with the DOWN force setting, i.e., the value read from the DOWN force potentiometer at block 526. If not, the routine disables use of the MinSpeed Register at block 520 and loads the MinSpeed Register with the UP force setting from the force potentiometer at block 522.

[0088] The routine continues at block 528 where the routine subtracts 20 from the MinSpeed value. The MinSpeed value ranges from 0 to 63. The system uses 64 levels of force. If the result is negative at block 530, the routine clears the MinSpeed Register at block 532 to effectively truncate the lower 38 percent of the force settings. If no, the routine divides the minimum speed by 4 to scale 8 speeds to 32 force settings at block 534. At block 536, the routine adds 4 into the minimum speed to correct the offset, and clips the result to a maximum of 12. At block 538 the routine enables use of the MinSpeed Register.

[0089] At block 540 the routine checks if the period of the rectified AC line signal (input to microprocessor 300 at pin P24) is less than 9 milliseconds (indicating the line frequency is 60 Hz). If it is, the routine skips to block 548. If not, the routine checks if the light shut-off timer is active at block 542. If not, the routine skips to block 548. If yes, the routine checks if the light time value is greater than 2.5 minutes at block 544. If no, the routine skips to block 548. If yes, the routine calls the SetVarLight subroutine (see FIG. 8), to correct the light timing setting, at block 546.

[0090] At block 548 the routine checks if the radio signal has been clear for 100 milliseconds or more. If not, the routine skips to block 552. If yes, the routine clears the radio at block 550. At block 552, the routine resets the watchdog timer. At block 554, the routine loops to the beginning of the main loop.

[0091] The SetVarLight subroutine, FIG. 8, is called whenever the door is commanded to move and the worklight is to be turned on. When the SetVarLight subroutine, block 558 is called, the subroutine checks if the period of the rectified power line signal (pin P24 of microprocessor 300) is greater than or equal to 9 milliseconds. If yes, the line frequency is 50 Hz, and the timer is set to 2.5 minutes at block 564. If no, the line frequency is 60 Hz and the timer is set to 4.5 minutes at block 562. After setting, the subroutine returns to the call point at block 566.

[0092] The hardware timer interrupt subroutine operated by microprocessor 300, shown at block 422, runs every 0.256 milliseconds. Referring to FIGS. 9A-9C, when the subroutine is first called, it sets the radio interrupt status as indicated by the software flags at block 580. At block 582, the subroutine updates the software timer extension. The next series of steps monitor the AC power line frequency (pin P24 of microprocessor 300). At step 584, the subroutine checks if the rectified power line input is high (checks for a leading edge). If yes, the subroutine skips to block 594, where it increments the power line high time counter, then continues to block 596. If no, the subroutine checks if the high time counter is below 2 milliseconds at block 586. If yes, the subroutine skips to block 594. If no, the subroutine sets the measured power line time in RAM at block 588. The subroutine then resets the power line high time counter at block 590 and resets the phase timer register in block 592.

[0093] At block 596, the subroutine checks if the motor power level is set at 100 percent. If yes, the subroutine turns on the motor phase control output at block 606. If no, the subroutine checks if the motor power level is set at 0 percent at block 598. If yes, the subroutine turns off the motor phase control output at block 604. If no, the phase timer register is decremented at block 600 and the result is checked for sign. If positive the subroutine branches to block 606; if negative the subroutine branches to block 604.

[0094] The subroutine continues at block 608 where the incoming RPM signal (at pin P31 of microprocessor 300) is digitally filtered. Then the time prescaling task switcher (which loops through 8 tasks identified at blocks 620, 630, 640, 650) is incremented at block 610. The task switcher varies from 0 to 7. At block 612, the subroutine branches to the proper task depending on the value of the task switcher.

[0095] If the task switcher is at value 2 (this occurs every 4 milliseconds), the execute motor state machine subroutine is called at block 620. If the task is value 0 or 4 (this occurs every 2 milliseconds), the wall control switches are debounced at block 630. If the task value is 6 (this occurs every 4 milliseconds), the execute 4 ms timer subroutine is called at block 640. If the task is value 1, 3, 5 or 7, the 1 millisecond timer subroutine is called at block 650. Upon completion of the called subroutine, the 0.256 millisecond timer subroutine returns at block 614.

[0096] Details of the 1 ms timer subroutine (block 650) are shown in FIGS. 10A-10C. When this subroutine is called, the first step is to update the A/D converters on the UP and DOWN force setting potentiometers (P34 and P35 of microprocessor 300) at block 652. At block 654, the subroutine checks if the A/D conversion (comparison at comparators 320 and 322) is complete. If yes, the measured potentiometer values are stored at block 656. Then the stored values (which vary from 0 to 127) are divided by 2 to obtain the 64 level force setting at block 658. If no, the subroutine decrements the infrared obstacle detector timeout timer at block 660. In block 662, the subroutine checks if the timer has reached zero. If no, the subroutine skips to block 672. If yes, the subroutine resets the infrared obstacle detector timeout timer at block 664. The flag setting for the obstacle detector signal is checked at block 666. If no, the one-shot break flag is set at block 668. If yes, the flag is set indicating the obstacle detector signal is absent at block 670.

[0097] At block 672, the subroutine increments the radio time out register. Then the infrared obstacle detector reversal timer is decremented at block 674. The pass point input is debounced at block 676. The 125 millisecond prescaler is incremented at block 678. Then the prescaler is checked if it has reached 63 milliseconds at block 680. If yes, the fault blinking LED is updated at block 682. If no, the prescaler is checked if it has reached 125 ms at block 684. If yes, the 125 ms timer subroutine is executed at block 686. If no, the routine returns at block 688.

[0098] The 125 millisecond timer subroutine (block 690) is used to manage the power level of the motor 118. At block 692, the subroutine updates the RS-232 mode timer and exits the RS-232 mode timer if necessary. The same pair of wires is used for both wall control switches and RS-232 communication. If RS-232 communication is received while in the wall control mode, the RS-232 mode is entered. If four seconds passes since the last RS-232 word was received, then the RS-232 timer times out and reverts to the wall control mode. At block 694 the subroutine checks if the motor is set to be stopped. If yes, the subroutine skips to block 716 and sets the motor's power level to 0 percent. If no, the subroutine checks if the pre-travel safety light is flashing at block 696 (if the optional flasher module has been installed, a light will flash for 2 seconds before the motor is permitted to travel and then flash at a predetermined interval during motor travel). If yes, the subroutine skips to block 716 and sets the motor's power level to 0 percent.

[0099] If no, the subroutine checks if the microprocessor 300 is in the last phase of a limit training mode at block 698. If yes, the subroutine skips to block 710. If no, the subroutine checks if the microprocessor 300 is in another part of the limit training mode at block 700. If no, the subroutine skips to block 710. If yes, the subroutine checks if the minimum speed (as determined by the force settings) is greater than 40 percent at block 704. If no, the power level is set to 40 percent at block 708. If yes, the power level is set equal to the minimum speed stored in MinSpeed Register at block 706.

[0100] At block 710 the subroutine checks if the flag is set to slow down. If yes, the subroutine checks if the motor is running above or below minimum speed at block 714. If above minimum speed, the power level of the motor is decremented one step increment (one step increment is preferably 5% of maximum motor speed) at block 722. If below the minimum speed, the power level of the motor is incremented one step increment (which is preferably 5% of maximum motor speed) to minimum speed at block 720.

[0101] If the flag is not set to slow down at block 710, the subroutine checks if the motor is running at maximum allowable speed at block 712. If no, the power level of the motor is incremented one step increment (which is preferably 5% of maximum motor speed) at block 720. If yes, the flag is set for motor ramp-up speed complete.

[0102] The subroutine continues at block 724 where it checks if the period of the rectified AC power line (pin P24 of microprocessor 300) is greater than or equal to 9 ms. If no, the subroutine fetches the motor's phase control information (indexed from the power level) from the 60 Hz look-up table stored in ROM at block 728. If yes, the subroutine fetches the motor's phase control information (indexed from the power level) from the 50 Hz look-up table stored in ROM at block 726.

[0103] The subroutine tests for a user enable/disable of the infrared obstacle detector-controlled worklight feature at block 730. Then the user radio learning timers, ZZWIN (at the wall keypad if installed) and AUXLEARNSW (radio on air and worklight command) are updated at block 732. The software watchdog timer is updated at block 734 and the fault blinking LED is updated at block 736. The subroutine returns at block 738.

[0104] The 4 millisecond timer subroutine is used to check on various systems which do not require updating as often as more critical systems. Referring to FIGS. 12A and 12B, the subroutine is called at block 640. At block 750, the RPM safety timers are updated. These timers are used to determine if the door has engaged the floor. The RPM safety timer is a one second delay before the operator begins to look for a falling door, i.e., one second after stopping. There are two different forces used in the garage door operator. The first type force are the forces determined by the UP and DOWN force potentiometers. These force levels determine the speed at which the door travels in the UP and DOWN directions. The second type of force is determined by the decrease in motor speed due to an external force being applied to the door (an obstacle or the floor). This programmed or pre-selected external force is the maximum force that the system will accept before an auto-reverse or stop is commanded.

[0105] A block 752 the 0.5 second RPM timer is checked to see if it has expired. If yes, the 0.5 second timer is reset at block 754. At block 756 safety checks are performed on the RPM seen during the last 0.5 seconds to prevent the door from falling. The 0.5 second timer is chosen so the maximum force achieved at the trolley will reach 50 kilograms in 0.5 seconds if the motor is operating at 100 percent of power.

[0106] At block 758, the subroutine updates the 1 second timer for the optional light flasher module. In this embodiment, the preferred flash period is 1 second. At block 760 the radio dead time and dropout timers are updated. At block 762 the learn switch is debounced. At block 764 the status of the worklight is updated in accordance with the various light timers. At block 766 the optional wall control blink timer is updated. The optional wall control includes a light which blinks when the door is being commanded to auto-reverse in response to an infrared obstacle detector signal break. At block 768 the subroutine returns.

[0107] Further details of the asynchronous RPM signal interrupt, block 434, are shown in FIGS. 13A and 13B. This signal, which is provided to microprocessor 300 at pin P31, is used to control the motor speed and the position detector. Door position is determined by a value relative to the pass point. The pass point is set at 0. Positions above the pass point are negative; positions below the pass point are positive. When the door travels to the UP limit, the position detector (or counter) determines the position based on the number of RPM pulses to the UP limit number. When the door travels DOWN to the DOWN limit, the position detector counts the number of RPM pulses to the DOWN limit number. The UP and DOWN limit numbers are stored in a register.

[0108] At block 782 the RPM interrupt subroutine calculates the period of the incoming RPM signal. If the door is traveling UP, the subroutine calculates the difference between two successive pulses. If the door is traveling DOWN, the subroutine calculates the difference between two successive pulses. At block 784, the subroutine divides the period by 8 to fit into a binary word. At block 786 the subroutine checks if the motor speed is ramping up. This is the max force mode. RPM timeout will vary from 10 to 500 milliseconds. Note that these times are recommended for a DC motor. If an AC motor is used, the maximum time would be scaled down to typically 24 milliseconds. A 24 millisecond period is slower than the breakdown RPM of the motor and therefore beyond the maximum possible force of most preferred motors. If yes, the RPM timeout is set at 500 milliseconds (0.5 seconds) at block 790. If no, the subroutine sets the RPM timeout as the rounded-up value of the force setting in block 788.

[0109] At block 792 the subroutine checks for the direction of travel. This is found in the state machine register. If the door is traveling DOWN, the position counter is incremented at block 796 and the pass point debouncer is sampled at block 800. At block 804, the subroutine checks for the falling edge of the pass point signal. If the falling edge is present, the subroutine returns at block 814. If there is a pass point falling edge, the subroutine checks for the lowest pass point (in cases where more than one pass point is used). If this is not the lowest pass point, the subroutine returns at block 814. If it is the only pass point or the lowest pass point, the position counter is zeroed and the subroutine returns at block 814.

[0110] If the door is traveling UP, the subroutine decrements the position counter at block 794 and samples the pass point debouncer at block 798. Then it checks for the rising edge of the pass point signal at block 802. If there is no pass point signal rising edge, the subroutine returns at block 814. If there is, it checks for the lowest pass point at block 806. If no the subroutine returns at block 814. If yes, the subroutine zeroes the position counter and returns at block 814.

[0111] The motor state machine subroutine, block 620, is shown in FIG. 14. It keeps track of the state of the motor. At block 820, the subroutine updates the false obstacle detector signal output, which is used in systems that do not require an infrared obstacle detector. At block 822, the subroutine checks if the software watchdog timer has reached too high a value. If yes, a system reset is commanded at block 824. If no, at block 826, it checks the state of the motor stored in the motor state register located in EEPROM 302 and executes the appropriate subroutine.

[0112] If the door is traveling UP, the UP direction subroutine at block 832 is executed. If the door is traveling DOWN, the DOWN direction subroutine is executed at block 828. If the door is stopped in the middle of the travel path, the stop in midtravel subroutine is executed at block 838. If the door is fully closed, the DOWN position subroutine is executed at block 830. If the door is fully open, the UP position subroutine is executed at block 834. If the door is reversing, the auto-reverse subroutine is executed at block 836.

[0113] When the door is stopped in midtravel, the subroutine at block 838 is called, as shown in FIG. 15. In block 840 the subroutine updates the relay safety system (ensuring that relays K1 and K2 are open). The subroutine checks for a received wall command or radio command. If there is no received command, the subroutine updates the worklight status and returns. If yes, the motor power is set to 20 percent at block 844 and the motor state is set to traveling DOWN at block 846. The worklight status is updated and the subroutine returns at block 850. If the door is stopped in midtravel and a door command is received, the door is set to close. The next time the system calls the motor state machine subroutine, the motor state machine will call the DOWN direction subroutine. The door must close to the DOWN limit before it can be opened to the full UP limit.

[0114] If the state machine indicates the door is in the DOWN position (i.e. the DOWN limit position), the DOWN position subroutine, block 830, at FIG. 16 is called. When the door is in the DOWN position, the subroutine checks if a wall control or radio command has been received. If no, the subroutine updates the light and returns at block 858. If yes, the motor power is set to 20 percent at block 854 and the motor state register is set to show the state is traveling UP at block 856. The subroutine then updates the light and returns at block 858.

[0115] The UP direction subroutine, block 832, is shown in FIGS. 17A-17C. At block 860 the subroutine waits until the main loop refreshes the UP limit from EEPROM 302. Then it checks if 40 milliseconds have passed since closing of the light relay K3 at block 862. If not, the subroutine returns. If yes, the subroutine checks for flashing the warning light prior to travel at block 866 (only if the optional flasher module is installed). If the light is flashing, the status of the blinking light is updated and the subroutine returns at block 868. If not, the flashing is terminated, the motor UP relay is turned on at block 870. Then the subroutine waits until 1 second has passed after the motor was turned on at block 872. If no, the subroutine skips to block 888. If yes, the subroutine checks for the RPM signal timeout. If no, the subroutine checks if the motor speed is ramping up at block 876 by checking the value of the RAMPFLAG register in RAM (i.e., UP, DOWN, FULLSPEED, STOP). If yes, the subroutine skips to block 888. If no, the subroutine checks if the measured RPM is longer than the allowable RPM period at block 878. If no, the subroutine continues at block 888.

[0116] If the RPM signal has timed out at block 874 or the measured time period is longer than allowable at block 878, the subroutine branches to block 880. At block 880, the reason is set as force obstruction. At block 882, if the training limits are being set, the training status is updated. At block 884 the motor power is set to zero and the state is set as stopped in midtravel. At block 886 the subroutine returns.

[0117] At block 888 the subroutine checks if the door's exact position is known. If it is not, the door's distance from the UP limit is updated in block 890 by subtracting the UP limit stored in RAM from the position of the door also stored in RAM. Then the subroutine checks at block 892 if the door is beyond its UP limit. If yes, the subroutine sets the reason as reaching the limit in block 894. Then the subroutine checks if the limits are being trained. If yes, the limit training machine is updated at block 898. If no, the motor's power is set as zero and the motor state is set at the UP position in block 900. Then the subroutine returns at block 902.

[0118] If the door is not beyond its UP limit, the subroutine checks if the door is being manually positioned in the training cycle at block 904. If not, the door position within the slowdown distance of the limit is checked at block 906. If yes, the motor slow down flag is set at block 910. If the door is being positioned manually at block 904 or the door is not within the slow down distance, the subroutine skips to block 912. At block 912 the subroutine checks if a wall control or radio command has been received. If yes, the motor power is set at zero and the state is set at stopped in midtravel as block 916. If no, the system checks if the motor has been running for over 27 seconds at block 914. If yes, the motor power is set at zero and the motor state is set at stopped in midtravel at block 916. Then the subroutine returns at block 918.

[0119] Referring to FIG. 18, the auto-reverse subroutine block 836 is described. (Force reversal is stopping the motor for 0.5 seconds, then traveling UP.) At block 920 the subroutine updates the 0.5 second reversal timer (the force reversal timer described above). Then the subroutine checks at block 922 for expiration of the force-reversal timer. If yes, the motor power is set to 20 percent at block 924 and the motor state is set to traveling UP at block 926 and the subroutine returns at block 932. If the timer has not expired, the subroutine checks for receipt of a wall command or radio command at block 928. If yes, the motor power is set to zero and the state is set at stopped in midtravel at block 930, then the subroutine returns at block 932. If no, the subroutine returns at block 932.

[0120] The UP position routine, block 834, is shown in FIG. 19. Door travel limits training is started with the door in the UP position. At block 934, the subroutine updates the relay safety system. Then the subroutine checks for receipt of a wall command or radio command at block 936 indicating an intervening user command. If yes, the motor power is set to 20 percent at block 938 and the state is set at traveling DOWN in block 940. Then the light is updated and the subroutine returns at block 950. If no wall command has been received, the subroutine checks for training the limits at block 942. If no, the light is updated and the subroutine returns at block 950. If yes, the limit training state machine is updated at block 944. Then the subroutine checks if it is time to travel DOWN at block 946. If no, the subroutine updates the light and returns at block 950. If it is time to travel DOWN, the state is set at traveling DOWN at block 948 and the system returns at block 950.

[0121] The DOWN direction subroutine, block 828, is shown in FIGS. 20A-20D. At block 952, the subroutine waits until the main loop routine refreshes the DOWN limit from EEPROM 302. For safety purposes, only the main loop or the remote transmitter (radio) can access data stored in or written to the EEPROM 302. Because EEPROM communication is handled within software, it is necessary to ensure that two software routines do not try to communicate with the EEPROM at the same time (and have a data collision). Therefore, EEPROM communication is allowed only in the Main Loop and in the Radio routine, with the Main loop having a busy flag to prevent the radio from communicating with the EEPROM at the same time. At block 954, the subroutine checks if 40 milliseconds has passed since closing of the light relay K3. If no, the subroutine returns at block 956. If yes, the subroutine checks if the warning light is flashing (for 2 seconds if the optional flasher module is installed) prior to travel at block 958. If yes, the subroutine updates the status of the flashing light and returns at block 960. If no, or the flashing is completed, the subroutine turns on the DOWN motor relay K2 at block 962. At block 964 the subroutine checks if one second has passed since the motor is first turned on. The system ignores the force on the motor for the first one second. This allows the motor time to overcome the inertia of the door (and exceed the programmed force settings) without having to adjust the programmed force settings for ramp up, normal travel and slow down. Force is effectively set to maximum during ramp up to overcome sticky doors.

[0122] If the one second time has not passed, the subroutine skips to block 984. If the one second time limit has passed, the subroutine checks for the RPM signal time out at block 966. If no, the subroutine checks if the motor speed is currently being ramped up at block 968 (this is a maximum force condition). If yes, the routine skips to block 984. If no, the subroutine checks if the measured RPM period is longer than the allowable RPM period. If no, the subroutine continues at block 984.

[0123] If either the RPM signal has timed out (block 966) or the RPM period is longer than allowable (block 970), this is an indication of an obstruction or the door has reached the DOWN limit position, and the subroutine skips to block 972. At block 972, the subroutine checks if the door is positioned beyond the DOWN limit setting. If it is, the subroutine skips to block 990 where it checks if the motor has been powered for at least one second. This one second power period after the DOWN limit has been reached provides for the door to close fully against the floor. This is especially important when DC motors are used. The one second period overcomes the internal braking effect of the DC motor on shut-off. Auto-reverse is disabled after the position detector reaches the DOWN limit.

[0124] If the motor has been running for one second, at block 990, the subroutine sets the reason as reaching the limit at block 994. The subroutine then checks if the limits are being trained at block 998. If yes, the limit training machine is updated at block 1002. If no, the motor's power is set to zero and the motor state is set at the DOWN position in block 1006. In block 1008 the subroutine returns.

[0125] If the motor has not been running for at least one second at block 990, the subroutine sets the reason as early limit at block 1026. Then the subroutine sets the motor power at zero and the motor state as auto-reverse at block 1028 and returns at block 1030.

[0126] Returning to block 984, the subroutine checks if the door's position is currently unknown. If yes, the subroutine skips to block 1004. If no, the subroutine updates the door's distance from the DOWN limit using internal RAM in microprocessor 300 in block 986. Then the subroutine checks at block 988 if the door is three inches beyond the DOWN limit. If yes, the subroutine skips to block 990. If no, the subroutine checks if the door is being positioned manually in the training cycle at block 992. If yes, the subroutine skips to block 1004. If no, the subroutine checks if the door is within the slow DOWN distance of the limit at block 996. If no, the subroutine skips to block 1004. If yes, the subroutine sets the motor slow down flag at block 1000.

[0127] At block 1004, the subroutine checks if a wall control command or radio command has been received. If yes, the subroutine sets the motor power at zero and the state as auto-reverse at block 1012. If no, the subroutine checks if the motor has been running for over 27 seconds at block 1010. If yes, the subroutine sets the motor power at zero and the state at auto-reverse. If no, the subroutine checks if the obstacle detector signal has beer, missing for 12 milliseconds or more at block 1014 indicating the presence of the obstacle or the failure of the detector. If no, the subroutine returns at block 1018. If yes, the subroutine checks if the wall control or radio signal is being held to override the infrared obstacle detector at block 1016. If yes, the subroutine returns at block 1018. If no, the subroutine sets the reason as infrared obstacle detector obstruction at block 1020. The subroutine then sets the motor power at zero and the state as auto-reverse at block 1022 and returns at block 1024. (The auto-reverse routine stops the motor for 0.5 seconds then causes the door to travel up.)

[0128] The appendix attached hereto includes a source listing of a series of routines used to operate a movable barrier operator in accordance with the present invention.

[0129] While there has been illustrated and described a particular embodiment of the present invention, it will be appreciated that numerous changes and modifications will occur to those skilled in the art, and it is intended in the appended claims to cover all those changes and modifications which followed in the true spirit and scope of the present invention. APPENDIX PRO7000 DC Motor Operator Manual forces, automatic limits New learn switch for learning the limits Code based on Flex GDO Notes: Motor is controlled via two Form C relays to control direction Motor speed is controlled via a fet (2 IRF540's in parallel) with a phase control PWM applies. Wall control (and RS232) are P98 with a redundant smart button and command button on the logic board Flex GDO Logic Board Fixed AND Rolling Code Functionality Learn from keyless entry transmitter Posi-lock Turn on light from broken IR beam (when at up limit) Keyless entry temporary password based on number of hours or number of activations. (Rolling code mode only) GDO is initialized to a ‘clean slate’ mode when the memory is erased. In this mode, the GDO will receive either fixed or rolling codes. When the first radio code is learned, the GDO locks itself into that mode (fixed or rolling) until the memory is again erased. Rolling code derived from the Leaded67 code Using the 8K zilog 233 chip Timer interrupt needed to be 2X faster Revision history Revision 1.1:  Changed light from broken IP beam to work in both fixed and rolling  modes.  Changed light from IR beam to work only on beam break, not on beam  block. Revision 1.2:  Learning rolling code formely erased fixed code. Mode is now  determined by first transmitter learned after radio erase. Revision 1.3:  Moved radio interrupt disable to reception of 20 bits.  Changed mode of radio switching. Formely toggled upon radio error,  now switches in pseudo-random fashion depending upon value of  125 ms timer. Revision 1.4:  Optimized portion of radio after bit value is determined. Used  relative addressing to speed code and minimize ROM size. Revision 1.5:  Changed mode of learning transmitters. Learn command is now  light-command, learn light is now light-lock, and learn open/close/  stop is lock-command. (Command was press light, press command,  release light, release command, worklight was press light, press  command,  release command, release light, o/c/s was press lock, press command,  release command, release lock. This caused DOG2 to reset Revision 1.6:  Light button and light transmitter now ignored during travel.  Switch data cleared only after a command switch is checked. Revision 1.7:  Rejected fixed mode (and fixed mode test) when learning light and open/close/stop transmitters. Revision 1.8:  Changed learn from wall control to work obly when both switches are  held. Modified force pot. read routine (moved enabling of blank  time and disabling of interrupts. Fixed mode now learns command  with any combination of wall control switches. Revision 1.9:  Changed PWM output to go from 0-50% duty cycle. This eliminated  the problem of PWM interrupts causing problems near 100% duty cycle.  THIS REVISION REQUIRES A HARDWARE CHANGE. Revision 1.9A:  Enabled ROM chacksum. Cleaned up documentation. Revision 2.0:  Blank tire noise immunity,. If noise signal is detected during blank time  the data already received is not thrown out. The data is retained, and the  noise pulse identified as such. The interrupt is enabled to contine to look  for the sync pulse. Revision 2.0A:  On the event that the noise pulse is of the same duration as the sync  pulse the time between sync and first data pulse (inactive time) is  measured The inactive time is 5.14 ms for billion code and 2.4 ms for  rolling code. If it is determined that the previously received sync is  indeed a noise pulse, the pulse is thrown out and the micro continules to  lock for a sync pulse as in Rev. 2.0. Revision 2.1:  To make the blank time more impervious to noise, the sync pulses are  differentiated between. Fixed max width is 4.6 ms, roll max width is 2.3  ms.  This is simular to the inactive time check done in Rev. 2.0A. Revision 2.2:  The worklight function; when the IP beam is broken and the door is at  the up limit the light will turn on for 4.5 min. This revision allows  the worklight function to be enabled and disabled by the user. The  function will come enabled from the factor.  To disable, with the light off press and hold the light button for 7 sec.  The light will come on and after 5 sec. the function is disabled the light  will turn off. To enable the function, turn the light on, release the  button, then press and hold the light button down for 5 sec. The light  will turn off and after the function has been enable in 5 sec.  the light will turn on. Revision 3.0:  Integrated in functionality for Siminor rolling code transmitter. The  Siminor transmitter may be received whenever a C code transmitter may  be received.  Siminor transmitters are able to perform as a standard command or as a  light control transmitter, but not as an open/close/stop transmitter. Revision 3.1:  Modified handling of rolling code counter (in mirroring and adding) to  improve efficiency and hopefully kill all short cycles when a radio is  jammed on the  air. PROD000 Revision 0.1:  Removed physical radio tests  Disabled radio temporarily  Put in sign bit test for limits  Automatic limits working Revision 0.2:  Provided for traveling up when too close to limit Revision 0.3:  Changed force pot. read to new routine.  Disabled T1 interrupt and all old force pot. code  Disabled all RS232 output Revision 0.4:  Added in (veerrrry) rough force into pot. read routine Revision 0.5:  Changed EEPROM in comments to add in up limit, last operation, and  down limit.  Created OnePass register  Added in limit read from nonvolatile when going to a moving state  Added in limit read on power-up  Created passcounter register to keep track of pass point(s)  Installed basic wake-up routine to restore position based on last state Revision 0.6:  Changed RPM time read to routine used in P98 to save RAM  Changed operation of RPM forced up travel  Implemented pass point for one-pass-point travel Revision 0.7:  Changed pass point from single to multiple (no EEPROM support) Revision 0.8:  Changed all CKIPRADIO loads from 0xFF to NOEECOMM  Installed EEPROM support for multiple pass points Revision 0.9:  Changed state machine to handle wake-up (i.e. always head towards  the lowest pass point to re-orient the GDO) Revision 0.10:  Changed the AC line input routine to work off full-wave rectified  AC coming in Revision 0.11: Installed the phase control for motor speed control Revision 0.12:  Installed traveling down if too near up limit  Installed speed-up when starting travel  Installed slow-down when ending travel Revision 0.13:  Re-activated the C code Revision 0.14:  Added in conditional assembly for Siminor radio codes Revision 0.15:  Disabled old wall control code  Changed all pins to conform with new layout  Removed unused constants  Commented out old wall control routine  Changed code to run at 6 MHz Revision 0.16:  Fixed bugs in Flex radio Revision 0.17:  Re-enabled old wall control. Changed command charging time to 12 ms  to fix FMEA problems with IR protectors. Revision 0.18:  Turned on learn switch connected to EEPROM clock line Revision 0.19  Eliminated unused registers  Moved new registers out of radio group  Re-enabled radio interrupt Revision 0.20  Changed limit test to account for “lost” position  Re-wrote pass point routine Revision 0.21  Changed limit tests in state setting routines  Changed criteria for looking for lost position  Changed lost operation to stop until position is known Revision 0.22:  Added in L_A_C state machine to learn the limits   Installed learn-command to go into LAC mode   Added in command button and learn button jog commands   Disabled limit testing when in learn mode   Added in LED flashing for in learn mode   Added in EVERYTHING with respect to learning limits Note: LAC still isn't working properly!!! Revision 0.23:  Added in RS232 functionality over wall control lines Revision 0.24:  Touched up RS232 over wall control routine  Removed 50 Hz force table  Added in fixes to LAC state machine Revision 0.25:  Added switch set and release for wall control (NOT smart switch,  into RS232 commands (Turned debouncer set and release in to subs)  Added smart switch into RS232 commands (smart switch is also a sub)  Re-enabled pass point test in ‘:’ RS232 command  Disabled smart switch scan when in RS232 mode  Corrected relative reference in debouncer subroutines  RS232 ‘F’ command still needs to be fixed Revision 0.26:  Added in max. force operation until motor ramp-up is done  Added in cleaning of slowdown flag in set_any routine  Changed RPM timeout from 30 to 60 ms Revision 0.27:  Switched phase control to off, then on (was on, then off) inside  each half cycle of the AC line (for noise reduction)  Changed from 40 ms unit max. period to 32 (will need further changes)  Fixed bug in force ignore during ramp (previously jumped from down to  up state machine)  Added in complete force ignore at very slow part of ramp (need to  change this to ignore when very close to limit)  Removed that again  Bug fix -- changed force skip during ramp-up. Before, it kept counting  down the force ignore timer. Revision 0.28:  Modified the wall control documentation  Installed blinking the wall control on an IP reversal instead of the  worklight  Installed blinking the wall control when a pass point is seen Revision 0.29:  Changed max. RPM timeout to 100 ms  Fixed wall control blink bug  Raised minimum speed setting NOTE: Forces still need to be set to accurate levels Revision 0.30:  Removed ‘er’ before setteing of pcon register  Bypassed slow-down to limit during learn mode Revision 0.31:  Changed force ramp to a linear FORCE ramp, not a linear time ramp   Installed a look-up table to make the ramp more linear.  Disabled interrupts during radio pointer match  Changed slowdown flag to a up-down-stop ramping flag Revision 0.32:  Changed down limit to drive lightly into floor  Changed down limit when learning to back off of floor for a few pulses Revision 0.33:  Changed max. speed to ⅔ when a short door is detected Revision 0.34:  Changed light timer to 2.5 minutes for a 50 Hz line, 4.5 minutes for  a 60 Hz line. Currently, the light timer is 4.5 minutes WHEN THE  UNIT FIRST POWERS UP.  Fixed problem with learning RP set to and extended group Revision 0.35:  Changed strting postion of pass point counter to 0x30 Revision 0.36:  Changed algorithm for finding down limit to cure stopping at the floor  during the learn cycle  Fixed bug in learning limits: Up limit was being updated from  EEPROM during the learn cycle  Changed method of checking when limit is reached: calculation for  distance to limit is now ALWAYS performed  Added in skipping of limit test when position is lost Revision 0.37:  Revised minimum travel distance and short door constants to reflect  approximately 10 RPM pulses/min Revision 0.38:  Moved slowstart number closer to the limit  Changed backoff number from 10 to 8 Revision 0.39:  Changed backoff number from 8 to 12 Revision 0.40:  Changed task switcher to unburden processor  Consolidated tasks 0 and 4  Took extra unused code out of tasks 1, 3, 5, 7  Moved auz light and 4 ms timer into task 6  Put state machine into task 2 only  Adjusted auto_delay, motdel, rpm_time_out, force_ignore,  motor_timer, obs_count, for new state machine tick  Removed force_pre prescaler (no longer needed with 4 ms state  machine)  Moved updating of obs_count to one ms timer for accuracy  Changed autoreverse delay timer into a byte-wide timer because it was  only storing an 8 bit number anyways . . .  Changed flash delay and light timer constants to adjust for 4 ms tick Revision 0.41:  Switched back to 4 MHz operation to account for the fact that Zilog's  Z86733 OTP won't run at 6 MHz reliably Revision 0.42:  Extended RPM timer so that it could measure from 0-524 ms with  a resolution of 8 us Revision 0.43:  Put in the new look-up table for the force pots (max RPM pulse period  multiplied by 20 to scale it for the various speeds).  Removed taskswitch because it was a redundant register  Removed extra call to the auxlight routine  Removed register ‘temp’ because, as far as I can tell, it does nothing  Removed light_pre register  Eliminated ‘phase’ register because it was never used  Put in preliminary divide for scaling the force and speed  Created speedlevel AND IDEAL speed registers, which are not yet used Revision 0.47:  Undid the revision of revisions 0.44 through 0.46  Changed ramp-up and ramp-down to an adaptive ramp system  Changed force compare from subtract to a compare  Removed force ignore during ramp (was a kludge)  Changed max. RPM time out to 500 ms static  Put WDT kick in just before main loop  Fixed the word-wise T0EXT register  Set default RPM to max. to fix problem of not ramping up Revision 0.48:  Took out adaptive ramp  Created look-ahead speed feedback in RPM pulses Revision 0.49:  Removed speed feedback (again)  NOTE: Speed feedback isn't necessarily impossible, but after all my   efforts, I've concluded that the design time necessary ( a large   amount isn't worth the benefit it gives, especially given the   current time costraints of this project Revision 0.50:  Fixed the force pot read to actually return a value of 0-64  Set the msx. RPM period time out to be equivalent to the force setting Revision 0.51:  Added in P2M_SHADOW register to make the following posible:  Added in flashing warning light ‘with auto-detect) Revision 0.52:  Fixed the variable worklight timer to have the correct value on  power up  Re-enabled the reason register and stackreason  Enabled up limit to back off by one pulse if it appears to be  crashing the up stop port.  Set the door to ignore commands and radio when lost  Changed start of down ramp to 220  Changed backoff from 12 to 9  Changed drive-past of down limit to 9 pulses Revision 0.53:  Fixed RS232 ‘9’ and ‘F’ commands  Implemented RS232 ‘K’command  Removed ‘M’, ‘P’, and ‘S’ commands  Set the learn LED to always turn off at the end of the  learn limits mode Revision 0.54:  Reversed the direction of the pot. read to correct the direction  of the min. and max. forces when dialing the pots.  Added in “U” command (currently does nothing)  Aded in “V” command to read force pot. values Revision 0.55:  Changed number of pulses added in to down limit from 9 to 16 Revision 0.56:  Changed backoff number from 16 back to 9 (not 8!)  Changed minimum force/speed from 4/20 to 10/20 Revision 0.57:  Changed backoff number back to 16 again  Changed minimum force/speed from 10/20 back to 4/20  Changed learning speed from 10/20 to 20/20 Revision 0.58:  Changed learning speed from 20/20 to 12/20 (same as short door)  Changed force to max. during ramp-up period  Changed RPM timeout to a static vlaue of 500 ms  Change drive-past of limit from 1″ to 2″ of trolley travel  (Actually, changed the number from 10 pulses to 20 pulses)  Changed start of ramp-up from 1 to 4 (i.e. the power level)  Changed the alorithm when near the limit - the door will no  longer avoid going toward the limit, even if it is too close Revision 0.59:  Removed ramp-up bug from auto-reverse of GDO Revision 0.60:  Added in check for pass point counter if −1 to find position when lost  Change in waking up when lost. GDO now heads toward pass point  only on first operation after a power outage. Heads down on all  subsequent operations.  Created the “limit unknown” fault and prevented the GDO from  traveling whe the limits are not set at a reasonable value  Cleared the fault code on entering learn limits mode  Implemented RS232 ‘H’ command Revision 0.61:  Changed limit test to look for trolley exactly at the limit position  Changed search for pass point to erase limit memory  Changed setup position to 2″ above the pass point  Set the learn LED to turn off whenever the L_A_C is cleared  Set the learn limits mode to shut off whenever the worklight times out Revision 0.62:  Removed test for being exactly at down limit (it disabled the drive into  the limit feature  Fixed bug causing the GDO to ignore force when it should autoreverse  Added in ignoring commands when lost and traveling up Revision 0.63:  Installed MinSpeed register to vary minimum speed with force pot  setting  Created main loop routine to scale the min speed based on force pot.  Changed drive-past of down limit from 20 to 30 pulses 2″ to 3″ Revision 0.64:  Changed learnin algorithm to utilize block. (Changed autoreverse to  add in ½″ to position instead of backing trolley off of the floor)  Enabled ramp-down when nearing the up limit in learn mode Revision 0.65:  Put special case in speed check to enable slow down near the up limit Revision 0.66:  Changed ramp-up: Ramping up of speed is now constant - the ramp-  down is the only ramp affected by the force pot. setting  Changed ramp-up and ramp-down tests to ensure that the GDO will get  UP to the minimum speed when we are inside the ramp-down zone,  (The above change necessitated this)  Changed the down limit to add in 0.2″ instead of 0.5″ Revision 0.67:  Removed minimum travel test in set_arev_state  Moved minimum distance of down limit from pass point from 5″ to 2″  Disabled moving pass point when only one pass point has been set Revision 0.68:  Set error in learn state if no pass point is seen Revision 0.69:  Added in decrement of pass point counter in learn mode to kill bugs  Fixed bug: Force pots were being ignored in the learn mode  Added in filtering of the RPM (RPM _FILTER register and a routine in  the one ms timer)  Added in check of RPM filter inside RPM interrupt  Added in polling RPM pin inside RPM interrupt  Re-enabled stopping when in learn mode and position is lost Revision 0.70:  Removed old method of filtering RPM  Added in a “debouncer” to filter the RPM Revision 0.71:  Changed “debouncer” to automatically vetor low  whenever an RPM pulse is considered valid Revision 0.72:  Changed number of pulses added in to down limit to 0. Since the actual  down limit test checks for the position to be BEYOND the down limit  this is the equivalent of adding one pulse into the down limit Revision 0.74:  Undid the work of rev. 0.73  Changed number of pulses added in to down limit to 1. Noting the  comment in rev. 0.72, this mean that we are adding 2 pulses  Changed learning speed to vary between 8/20 and 12/20, depending  upon the force pot. setting Revision 0.75:  Installed power-up chip ID on P22, P23, P24, and P25  Note : ID is on P24, P23, and P22. P25 is a strobe to signal valid data   First chip ID is 001, with strobe it's 0001.  Changed set_any routine to re-enable the wall control just in case we  stopped while the wall control was being turned off (to avoid disabling  the wall control completely  Changed speed during learn mode to be ⅔ speed for first seven seconds,  then to slow down to the minimum speed to make the limit learning the  same as operation during normal travel. Revision 0.76:  Restored learning to operate only at 60% speed Revision 0.77:  Set unit to reverse off of floor and subtract 1″ of travel  Reverted to learning at 40%-60% of full speed Revision 0.78:  Changed rampflag to have a constant for running at full speed  USed the above change to simplify the force ignore routine  Also used it to change the RPM time out. The time out is now set equal  to the pot setting, except during the ramp up when it is set to 500 ms.  Changed highest force pot setting to be exactly equal to 500 ms. Revision 0.79:  Changed setup routine to reverse off block (yet again). Added in one  pulse.  Enabled RS232 version number return  Enabled ROM checksum. Cleaned up ducumentation Revision 1.1:  Tweaked light times for 8.192 ms prescale instead of 8.0 ms prescale  Changed compare statement inside setvarlight to ‘uge’ for consistency  Changed one-shot low time to 2 ms for power line  Changed one-shot low time to truly count falling-edge-to-falling-edge. Revision 1.2:  Eliminated testing for lost GDO in set_up_dir_state (is already taken  care of by set_on_dir_state.  Created special time for max. run motor timer in learn mode: 50  seconds Revision 1.3:  Fixed bug in set_any to fix stack imbalance  Changed short door discrimination point to 78″ Revision 1.4:  Changed second ‘di’ to ‘ei’ in KnowSimCode  Changed IR protector to ignore for first 0.5 second before travel  Changed blinking time constant to take it back to 2 seconds before  travel  Changed blinking code to ALWAYS flash during travel, with pre-travel  flash when module is properly detected  Put in bounds checking on pass point counter to keep it in line  Changed driving into down limit to consider the system list if floor not  seen Revision 1.5:  Changed blinking of wall control at pass point to be a one-shot timer  to correct problems with bad passpoint connections and stopping at pass  point to cause wall control ignore. Revision 1.6:  Fixed blinking of wall control when indicating IP protector recersal  to give the blink a true 50% duty cycle  Changed blinker output to output a constant high instead of pulsing  Changed P2S_POR to 1010 Indicate Siminor unit; Revision 1.7:  Disabled Siminor Radio  Changed P2S_POR to 1011 Indicate Lift-Master unit,  Added in one more conditional assembly point to avoid use of simradio  label Revision 1.8:  Re-enabled Siminor Radio  Changed P2S_POR back to 1010 Siminor  Re-fixed blinking of wall control LED for protector reversal  Changed blinking of wall control LED for indicating pass point  Fixed error in calculating highest pass point value  Fixed error in calculating lowest pass point value Revision 1.9:  Lengthened blink time for indicating pass point  Installed a max. travel distance when lost   Removed skipping up limit test when lost   Reset the position when lost and force reversing  Installed sample of pass point signal when changing states Revision 2.0:  Moved main loop test for max. travel distance (was causing a memory  fault before) Revision 2.1:  Changed limit test to use 11000000b instead of 10000000b to ensure  only setting up of limit when we're actually close Revision 2.2:  Changed minimum speed scanning to move it further down the pot.  rotation. Formula is now \\force − 24/41 + 4, truncated to 12  Changed max. travel test to be inside motor state mahine. Max travel  test calculates for limit position differently when system is lost.  Reverted limit test to use 10000000b  Changed some jp's to jr's to conserve code space  Changed loading of reason byte with 0 to clearing if reason byte (very  desperate for space) Revision 2.3:  Disabled Siminor Radio  Changed P2S_POR to 1011 (Lift-Master) Revision 2.4:  Re-enabled Siminor Radio  Changed P2S_POR to 1010 (Siminor)  Changed wall control LED to also flash during learn mode  Changed reaction to single pass point near floor. If only one pass point  is seen during the learn cycle, and it is too close to the floor, the  learn cycle will now fail.  Removed an ei from pass point when learning to avoid a race condition Revision 2.5:  Changed backing off of up limit to only occur during learn cycle. Backs  off by {fraction (1/2 )}″ if learn cycle force stops within ½″ of stop bolt.  Removed considering system lost if floor not seen.  Changed drive-past of down limit to 36 pulses (3″)  Added in clearing of power level whenever motor gets stopped (to turn  off the FET's sooner)  Added in a 40 ms delay (using the same MOTDEL register as for the  traveling states to delay the shut-off of the motor relay. This should  enable the motor to discharge some energy before the relay has to break  the current flow  Created STOPNOFLASH label - it looks like it should have been there  all along  Moved incrementing MOTDEL timer into head of state machine to  conserve space Revision 2.6:  Fixed back-off of up limit to back off in the proper direction.  Added in testing for actual stop state in back-off (before was always  backing off the limit)  Simplified testing for light being on in ‘set any’ routine; eliminated  lights register Revision 2.7: (Test-only revision)  Moved ei when testing for down limit  Eliminated testing for negative number in radio time calculation  Installed a primitive debouncer for the pass point (out of paranoia  Changed a pass point in the down direction to correspond to a position  of 1  Installed a temporary echo of the RPM signal on the blinker pin  Temporarily disabled POM checksum  Moved three subroutines before address 0101 to save space (2.2B)  Framed lock up using upforce and dnforce registers with di and ei to  prevent corruption of upforce or dnforce while doing math (2.7C)  Fixed error in definition of pot_count register (2.7C)  Disabled actual number check of RPM perdod for debug (2.7D)  Added in di at test_up_sw and test_dn_sw for ramping up period  (2.7D)  Set RPM_TIME_OUT to always be loaded to max value for debug  (2.7E)  Set RPM_TIME_OUT to round up by two instead of one (2.7F)  Removed 2.7E revision (2.7F)  Fixed RPM_TIME_OUT to round up in both the up and down  direction(2.7G)  Installed constant RS232 output of RPM_TIME_OUT register (2.7H)  Enabled RS232 ‘U’ and ‘V’ commands (2.7I)  Disabled constant output of 2.7H (2.7I)  Set PS232 ‘U’ to output RPM_TIME_OUT (2.7I)  Removed disable of actual RPM number check (2.7J)  Removed pulsing to indicate RPM interrupt (2.7J)  2.7J note -- need to remove ‘u’ command function Revision 2.8:  Remove interrupt enable before resetting rpm_time_out. This will  introduce roughly 30us of extra delay in time measurement, but should  take care of  nuisance stops.  Removed push-ing and pop-ing of RP in tasks 2 and 6 to save stack  space (2.8B)  Removed temporary functionality for ‘u’ command (2.8 Release)  Re-enabled ROM checksum (2.8 Release)

[0130] L_A_C State Machine 73 77 *******       **** *      * 72 74*     76* Back to *    * 70 Up Lim ----   ---- 71 ----  ---- Error ****** 75 Position  the limit NON-VOL MEMORY MAP 00 A0 D0 Multi-function transmitters 01 A0 D0 02 A1 D0 03 A1 D0 04 A2 D1 05 A2 D1 06 A3 D1 07 A3 D1 08 A4 D2 09 A4 D2 0A A5 D2 0B A5 D2 0C A6 D3 0D A6 D3 0E A7 D3 0F A7 D3 10 A8 D4 11 A8 D4 12 A9 D4 13 A9 D4 14 A10 D5 15 A10 D5 16 A11 D5 17 A11 D5 18 B D6 19 B D6 1A C D6 1B C D6 1C unused D7 1D unused D7 1E unused D7 1F unused D7 20 unused DTCP Keyless permanent 4 digit code 21 unused DTCID Keyless ID code 22 unused DTCR1 Keyless Roll value 23 unused DTCR2 24 unused DTCT Keyless temporary 4 digit code 25 unused Duration Keyless temporary duration Upper byte = Mode; hours activations Lower byte = # of hours activations 26 unused Radio type 77665544 33221100 00 = CMD 01 = LIGHT 10 = OPEN/CLOSE/STOP 27 unused Fixed/roll Upper word = fixed/roll byte Lower word = unsed 28 CYCLE COUNTER 1ST 16 BITS 29 CYCLE COUNTER 2ND 16 BITS 2A VACATION FLAG Vacation Flag , Last Operation 0000 XXXX in vacation 1111 XXXX out of vacation 2B A MEMORY ADDRESS LAST WRITTEN 2C IRLIGHHTADDR 4-22-97 2D Up Limit 2E Pass point counter / Last operating state 2F Down Limit 3C-3F Force Back trace RS232 DATA REASON 00 COMMAND 10 RADIO COMMAND 20 FORCE 30 AUX OBS 40 A REVERSE DELAY 50 LIMIT 60 EARLY LIMIT 70 MOTOR MAX TIME, TIME OUT 80 MOTOR COMMANDED OFF RPM CAUSING AREV 90 DOWN LIMIT WITH COMMAND HELD A0 DOWN LIMIT WITH THE RADIO HELD B0 RELEASE OF COMMAND OR RADIO AFTER A FORCED UP MOTOR ON DUE TO RPM PULSE WITHG MOTOR OFF STATE 00 AUTOREVERSE DELAY 01 TRAVELING UP DIRECTION 02 AT THE UP LIMIT AND STOPES 03 ERROR RESET 04 TRAVELING DOWN DIRECTION 05 AT THE DOWN LIMIT 06 STOPPED IN MID TRAVEL DIAG 1) AOBS SHORTED 2) AOBS OPEN / MISS ALIGNED 3) COMMAND SHORTED 4) PROTECTOR INTERMITTENENT 5) CALL DEALER NO RPM IN THE FIRST SECOND 6) RPM FORCED A REVERSE 7) LIMITS NOT LEAPNET YET DOG 2 DOG 2 IS A SECONDARY WATCHDOG USED TO RESET THE SYSTEM IF THE LOWEST LEVEL “MAINLOOP” IS NOT REACHED WITHIN A 3 SECOND Conditional Assembly GLOBALS ON ; Enable a symbol file Yes .equ 1 No .equ 0 TwoThirtyThree .equ Yes UseSiminor .equ Yes EQUATE STATEMENTS check_sum_value .equ 065H ; CRC checksum for ROM code TIMER_I_EN .equ 0CH ; TMR mask to start timer 1 MOTORTIME .equ .27000 / 4 ; Max. run for otor = 22 sec (4 ms tick, LACTIME .equ (500 / 4; ; Delay before learning limits is 0.5 seconds LEARNTIME .equ (50000 / 4 ; Max. run for motor in learn mode PWM_CHARGE .equ 0CH ; PWM state for old force pots. LIGHT .equ 0FFH ; Flag for light on constantly LIGHT_ON .equ 10000000B ; P0 pin turning on worklight MOTOR_UP .equ 01000000B ; P0 pin turning on the up motor MOTOR_DN .equ 00100000B ; P0 pin turning on the down motor UP_OUT .equ 00110000B ; P3 pin otput for up force pot. DOWN_OUT .equ 001000000B ; P3 pin output for down force pot. DOWN_COMP .equ 00000001B ; P0 pin input for down force pot. UP_COMP .equ 00000010B ; P0 pin input for up force pot. FALSEIR .equ 00000001B ; P2 pin for false AOBS output LINSINFIN .equ 00010000B ; P2 pin for reading in AC line PPoint Port .equ p2 ; Port for pass point input PassPoint .equ 00011000B ; B_t mask for pass point input PhasePrt .equ p0 ; Port for phase control output PhaseHigh .equ 00010000B ; Pin for controlling FET's CHARGE_SW .equ 10000000B ; P3 Pin for charging the wall control DIS_SW .equ 01000000B ; P3 Pin for discharging the wall control SWITCHES1 .equ 00001000B ; PC Pin for first wall control input SWITCHES2 .equ 00000100B ; P0 Pin for second wall control input P01M_INIT .equ 00000101B ; set mode p00-p03 in p04-p07 out P2M_INIT .equ 01011100B ; P2M initialization for operation P2M_POP .equ 01000000B ; P2M initialization of output of onip ID P3M_INIT .equ 00000011B ; set port3 p30-p33 input ANALOG mode P01S_INIT .equ 10000000B ; Set init. state as worklight on, motor off P2S_INIT .equ 00000110B ; Init p2 to have LED off P2S_POR .equ 00101010B ; P2 init to output a chip ID (P25, P24, P23, P22; P3S_INIT .equ 00000000B ; Init p3 to have everything off BLINK_PIN .equ 00000000B ; Pin which controls flasher module P2M_ALLOUTS .equ 01111100B ; Pins which need to be refreshed to outputs P2M_ALLINS .equ 01011000B ; Pins which need to be refreshed to inputs RsPerHalf .equ 104 ; RS232 period 1200 Baud half time 416uS RsPer Full .equ 208 ; RS232 period full time 832us RsPer1P22 .equ 00 ; RS232 period 1.22 unit times 1.025ms (00 = 256) FLASH .equ 0FFH ; WORKLIGHT .equ LIGHT_ON ; Pin for toggling state of worklight PPOINTPULSES .equ 897 ; Number of RPM pulses between pass points SetupPos .equ (65535-20) ; Setup position -- 2” above pass point CMD_TEST .equ 00 ; States for old wall control routine WL_TEST .equ 01 VAC_TEST .equ 02 CHARGE .equ 03 RSSTATUS .equ 04 ; Hold wall control okt. in RS232 mode WALLOFF .equ 05 ; Turn off wall control LED for blinks AUTO_REV .equ 00H ; States for GDO state machine UP_DIRECTION .equ 01H UP_POSITION .equ 02H DN_DIRECTION .equ 04H DN_POSITION .equ 05H STOP .equ 06H CMD_SW .equ 01H ; Flags for switches hit LIGHT_SW .equ 02H VAC_SW .equ 04H TRUE .equ 0FFH ; Generic constants FALSE .equ 00H FIXED_MODE .equ 10101010b ;Fixed mode radio ROLL_MODE .equ 01010101b ;Rolling mode radio FIXED_TEST .equ 00000000b ;Unsure of mode -- test fixed ROLL_TEST .equ 00000001b ;Unsure of mode -- test roll FIXED_MASK .equ FIXED_TEST ;Bit mask for fixed mode ROLL_MASK .equ ROLL_TEST ;Bit mask for rolling mode FIXTHR .equ 03H ;Fixed code decision threshold DTHR .equ 02H ;Rolling code decision threshold FIXSYNC . equ 08H ;Fixed code sync threshold DSYNC .equ 04h ;Rolling code sync threshold FIXBITS .equ 11 ;Fixed code number of bits DBITS .equ 21 ;Polling code number of bits EQUAL .equ 00 ;Counter compare result constants BACKWIN .equ FH ; FWDWIN .equ 80H OUTOFWIN .equ 0FFH AddressCounter .equ 27H AddressAPointer .equ 2BH CYCCOUNT .equ 28H TOUCHID .equ 21H ;Touch code ID TOUCHROLL .equ 22H ;Touch code roll value TOUCHPERM .equ 20H ;Touch code permanent password TOUCHTEMP .equ 24H ;Touch code temporary password DURAT .equ 25H ;Touch code temp. duration VERSIONNUM .equ 088H ;Version: PRO7000 V2.8 ;4-22-97 IRLIGHTADDR .EQU 20H ;work light feature on or off DISABLE .EQU 00H ;00 = disabled, FF = enabled ; RTYPEADDR .equ 26h ;Radio transmitter type VACATIONADDR .equ 2AH MODEADDR .equ 20H ;Rolling/Fixed mode in EEPROM ;High byte = don't care (now) ;Low byte = RadioMode flag UPLIMADDR .equ 2DH ;Address of up limit LASTSTATEADDR .equ 2EH ;Address of last state DNLIMADDR .equ 2FH ;Address of down limit NOEECOMM .equ 01111111b ;Flag: skip radio read/write NOINT .equ 10000000b ;Flag: skip radio interrupts RDROPTIME .equ 125 ;Radio drop-out time: 0.5s LRNOCS .equ 0AAH ;Learn open/close/stop BRECEIVED .equ 077H ;B code received flag LRNLIGHT .equ 0BBH ;Light command trans. LRNTEMP .equ 0CCH ;Learn touchcode temporary LRNDURTN .equ 0DDH ;Learn t.c. temp. duration REGLEARN .equ 0EEH ;Regular learn mode NORMAL .equ 00H ;Normal command trans. ENTER .equ 00H ;Touch code ENTER key POUND .equ 01H ;Touch code # key STAR .equ 02H ;Touch code * key ACTIVATIONS .equ 0AAH ;Number of activations mode HOURS .equ 055H ;Number of hours mode ;Flags for Ramp Flag Register STILL .equ 00h ; Motor not moving RAMPUP .equ 0AAH ; Ramp speed up to maximum RAMPDOWN .equ 0FFH ; Slow down the motor to minimum FULLSPEED .equ 0CCH ; Running at full speed UPSLOWSTART .equ 200 ; Distance (in pulses) from limit when slow- down DNSLOWSTART .equ 220 ; of GDO motor starts (for up and down direction) BACKOFF .equ 16 ; Distance (in pulses) to back trolley off of floor ; when learning limits by reversing off of floor SHORTDOOR .equ 936 ; Travel distance (in pulses) that discriminates a ; one piece door (slow travel) from a normal door ; (normal travel) (Roughly 76″ PERIODS AUTO_REV_TIME .equ 124 ; (4 ms prescale) MIN_COUNT .equ 02H ; pwm start point TOTAL_PWM_COUNT .equ 03FH ; pwm end = start + 2*total − 1 FLASH_TIME .equ 61 ; 0.25 sec flash time ; 4.5 MINUTE USA LIGHT TIMER USA_LIGHT_HI .equ 080H ; 4.5 MIN USA_LIGHT_LO .equ 0BEH ; 4.5 MIN ; 2.5 MINUTE EUROPEAN LIGHT TIMEP EURO_LIGHT_HI .equ 047H ; 2.5 MIN EURO_LIGHT_LO .equ 086H ; 2.5 MIN ONE_SEC .equ 0F4H ; WITH A /4 IN FRONT CMD_MAKE .equ 8 ; cycle count *10mS CMD_BREAK .equ (255-8) LIGHT_MAKE .equ 8 ; cycle count *11mS LIGHT_BREAK .equ (255-8) VAC_MAKE_OUT .equ 4 ; cycle cound *100mS VAC_BREAK_OUT .equ (255-4) VAC_MAKE_IN .equ 2 VAC_BREAK_IN .equ (255-2) VAC_DEL .equ 8 ; Delay 16 ms for vacation CMD_DEL_EX .equ 6 ; Delay 12 ms ( 5*2 + 2) VAC_DEL_EX .equ 50 ; Delay 100 ms PREDEFINED REG ALL_ON_IMR .equ 00111101b ; turn on int for timers rpm auxobs radio RETURN_IMR .equ 00111100b ; return on the IMR RadioImr .equ 00000001b ; turn on the radio only GLOBAL REGISTERS STATUS .equ 04H ; CMD_TEST 00 ; WL_TEST 01 ; VAC_TEST 02 ; CHARGE 03 STATE .equ 05H ; state register LineCtr .equ 06H ; RampFlag .equ 0⁻H ; Ramp up, ramp down, or stop AUTO_DELAY .equ 08H LinePer .equ 09H ; Period of AC line coming in MOTOR_TIMER_HI .equ 0AH MOTOR_TIMER_LO .equ 0BH MOTOR_TIMER .equ 0AH LIGHT_TIMER_HI .equ 0CH LIGHT_TIMER_LO .equ 0DH LIGHT_TIMER .equ 0CH AOBSF .equ 0EH PrevPass .equ 0FH CHECK_GRP .equ 10H check_sum .equ r0 ; check sum pointer rom_data .equ r1 test_adr_hi .equ r2 test_adr_lo .equ r3 test_adr .equ rr2 CHECK_SUM .equ CHECK_GRP+0 ; check sum reg for por ROM_DATA .equ CHECK_GRP+1 ; data read LIM_TEST_HI .equ CHECK_GRP−0 ; Compare registers for measuring LIM_TEST_LO .equ CHECK_GRP−1 ; distance to limit LIM_TEST .equ CHECK_GRP+0 : AUXLEARNSW .equ CHECK_GRP+2 ; RRTO .equ CHECK_GRP+3 ; RPM_ACOUNT .equ CHECK_GRP+4 ; to test for active rpm RS_COUNTEP .equ CHECK_GRP+5 ; rs232 byte counter RS232DAT .equ CHECK_GRP+6 ; rs232 data RADIO_CMI .equ CHECK_GRP+7 ; radio command R_DEAD_TIME .equ CHECK_GRP+8 ; FAULT .equ CHECK_GRP+9 ; VACFLAG .equ CHECK_GRP+10 ; VACATION mode flag VACFLASH .equ CHECK_GRP+11 VACCHANGE .equ CHECK_GRP+12 FAULTTIME .equ CHECK_GRP+13 FORCE_IGNORE .equ CHECK_GRP+14 FAULTCODE .equ CHECK_GRP+15 TIMER_GROUP .equ 20H position_hi .equ r0 position_lo .equ r1 position .equ rr0 up_limit_hi .equ r2 up_limit_lo .equ r3 up_limit .equ rr2 switch_delay .equ r4 obs_count .equ r6 rscommand .equ r9 rs_temp_hi .equ r10 rs_temp_lo .equ r11 rs_temp .equ rr10 POSITION_HI .equ TIMER_GROUP+0 POSITION_LO .equ TIMER_GROUP+1 POSITION .equ TIMER_GROUP+2 UP_LIMIT_HI .equ TIMER_GROUP+2 UP_LIMIT_LO .equ TIMER_GROUP+3 UP_LIMIT .equ TIMER_GROUP−2 SWITCH_DELAY .equ TIMER_GROUP+4 OnePass .equ TIMER_GROUP+5 OBS_COUNT .equ TIMER_GROUP+6 RsMode .equ TIMER_GROUP+7 Divisor .equ TIMER_GROUP+8 ; Number to divide by RSCOMMAND .equ TIMER_GROUP+9 RS_TEMP_HI .equ TIMER_GROUP+10 RS_TEMP_LO .equ TIMER_GROUP+11 RS_TEMP .equ TIMER_GROUP+10 PowerLevel .equ TIMER_GROUP+12 ; Current step in 20-step phase ramp-up PhaseTMR .equ TIMER_GROUP+13 ; Timer for turning on and off phase control PhaseTime .equ TIMER_GROUP+14 ; Current time reload value for phase timer MaxSpeed .equ TIMER_GROUP+15 ; Maximum speed for this kind of door LEARN EE GROUP FOR LOOPS ECT LEARNEE_GRP .equ 30H ; TEMPH .equ LEARNEE_GRP ; TEMPL .equ LEARNEE_GRP+1 ; PSM_SHADOW .equ LEARNEE_GRP+2 ; Readable shadow of P2M register LEARNDB .equ LEARNEE_GRP+3 ; learn debouncer LEARNT .equ LEARNEE_GRP+4 ; learn timer ERASET .equ LEARNEE_GRP+5 ; erase timer MTEMPH .equ LEARNEE_(—GRP+6) ; memory temp MTEMPL .equ LEARNEE_GRP+7 ; memory temp MTEMP .equ LEARNEE_GRP+8 ; memory temp SERIAL .equ LEARNEE_GRP+9 ; data to & from nonvol memory ADDRESS .equ LEARNEE_GRP+10 ; address for the serial nonvol memory ZZWIN .equ LEARNEE_GRP+11 ; radio 00 code window T0_OFLOW .equ LEARNEE_GRP+12 ; Third byte of T0 counter T0EXT .equ LEARNEE_GRP+13 ; t0 extend dec'd every t0 int T0ENTWORD .equ LEARNEE_GRP+12 ; Word-wide t0 extension T125MS .equ LEARNEE_GRP+14 ; 125mS counter SKIPRADIO .equ LEARNEE_GRP+15 ; flag to skip radio read, write if ; learn or vacation talking to it temph .equ r0 ; temp1 .equ r1 ; learndb .equ r3 ; learn debouncer learnt .equ r4 ; learn timer eraset .equ r5 ; erase timer mtemph .equ r6 ; memory temp mtemp1 .equ r7 ; memory temp mtemp .equ r8 ; memory temp serial .equ r9 ; data to and from nonvol mem address .equ r10 ; addr for serial nonvol memory zzwin .equ r11 ; t0_oflow .equ r12 ; Overflow counter for T0 t0ext .equ r13 ; t0extend dec'd every t0 int t0extword .equ rr12 ; Word-wide T0 extension t125ms .equ r14 ; 125mS counter skipradio .equ r15 ; flag to skip radio read, write if ; learn or vacation talking to it FORCE_GROUP .equ 40H dnforce .equ r0 upforce .equ r1 loopreg .equ r3 up_force_hi .equ r4 up_force_lo .equ r5 up_force .equ rr4 dn_force_hi .equ r6 dn_force_lo .equ r7 dn_force .equ rr6 force_add_hi .equ r8 force_add_lo .equ r9 force_add .equ rr8 up_temp .equ r10 dn_temp .equ r11 pot_count .equ r12 force_temp_of .equ r13 force_temp_hi .equ r14 force_temp_lo .equ r15 DNFORCE .equ 40H UPFORCE .equ 41H AOBSTEST .equ 42H LoopReg .equ 43H UP_FORCE_HI .equ 44H UP_FORCE_LO .equ 45H DN_FORCE_HI .equ 46H DN_FORCE_LO .equ 47H UP_TEMP .equ 4AH ON_TEMP .equ 4BH POT_COUNT .equ 40H FORCE_TEMP_OF .equ 40H FORCE_TEMP_HI .equ 4EH FORCE_TEMP_LO .equ 4FH RPM_GROUP .equ 50H rtypes2 .equ r0 stackflag .equ r1 rpm_temp_of .equ r2 rpm_temp_hi .equ r3 rpm_temp_hiword .equ rr2 rpm_temp_lo .equ r4 rpm_past_hi .equ r5 rpm_past _lo .equ r6 rpm_period_hi .equ r7 rpm_period_lo .equ r8 divcounter .equ r11 ; Counter for dividing RPM time rpm_count .equ r12 rpm_time_out .equ r13 RTypes2 .equ RPM_GROUP+2 STACKFLAG .equ RPM_GROUP+1 RPM_TEMP_OF .equ RPM_GROUP+2 ; Overflow for RPM Time RPM_TEMP_HI .equ RPM_GROUP+2 ; RPM_TEMP_HWORD .equ RPM_GROUP+2 ; High word of RPM Time RPM_TEMP_LO .equ RPM_GROUP+4 RPM_PAST_HI .equ RPM_GROUP+5 RPM_PAST_LO .equ RPM_GROUP+6 RPM_PERIOD_HI .equ RPM_GROUP+7 RPM_PERIOD_LO .equ RPM_GROUP+8 DN_LIMIT_HI .equ RPM_GROUP+9 ; DN_LIMIT_LO .equ RPM_GROUP+10 ; DIVCOUNTER .equ RPM_GROUP+11 ; Counter for divinding RPM time RPM_FILTER .equ RPM_GROUP+11 ; DOUBLE MAPPED register for filtering signal RPM_COUNT .equ RPM_GROUP+12 RPM_TIME_OUT .equ RPM_GROUP+13 BLINK_HI .equ RPM_GROUP+14 ; Blink timer for flashing the BLINK_LO .equ RPM_GROUP+15 ; about-to-travel warning light BLINK .equ RPM_GROUP+14 ; Word-wise blink timer RADIO GROUP RadioGroup .equ 60H ; RTemp .equ RadioGroup ; radio temp storage RTempH .equ RadioGroup+1 ; radio temp storage high RTempL .equ RadioGroup+2 ; radio temp storage low RTimeAH .equ RadioGroup+3 ; radio active time high byte RTimeAL .equ RadioGroup+4 ; radio active time low byte RTimeIH .equ RadioGroup+5 ; radio inactive time high byte RTimeIL .equ RadioGroup+6 ; radio inactive time low byte Radio1H .equ RadioGroup+7 ; sync 1 code storage Radio1L .equ RadioGroup+8 ; sync 1 code storage RadioC .equ RadioGroup+9 ; radio word count PointerH .equ RadioGroup+10 ; PointerL .equ RadioGroup+11 ; AddValueH .equ RadioGroup+12 ; AddValueL .equ RadioGroup+13 ; Radio3H .equ RadioGroup+14 ; sync 3 code storage Radio3L .equ RadioGroup+15 ; sync 3 code storage rtemp .equ r0 ; radio temp storage rtemph .equ r1 ; radio temp storage high rtemp1 .equ r2 ; radio temp storage low rtimeh .equ r3 ; radio active time high byte rtimea1 .equ r4 ; radio active time low byte rtimeih .equ r5 ; radio inactive time high byte rtimei1 .equ r6 ; radio inactive time low byte radio1h .equ r7 ; sync 1 code storage radio11 .equ r8 ; sync 1 code storage radioc .equ r9 ; radio word count pointerh .equ r10 ; pointer1 equ r11 ; pointer .equ rr11 ; Overall pinter for ROM addvalueh .equ r12 ; addvalue1 .equ r13 ; radio3h .equ r14 ; sync 3 code storage radio31 .equ r15 ; sync 3 code storage w2 .equ rr14 ; For Siminor revision CounterGroup .equ 070h ; counter group TestReg .equ CounterGroup ; Test area when dividing BitMask .equ CounterGroup+01 ; Mask for transmitters LastMatch .equ CounterGroup+02 ; last matching code address LoopCount .equ CounterGroup+03 ; loop counter CounterA .equ CounterGroup+04 ; counter translation MSB CounterB .equ CounterGroup+05 ; CounterC .equ CounterGroup+06 ; CounterD .equ CounterGroup+07 ; countr translation LSB MirrorA .equ CounterGroup+08 ; back translation MSB MirrorB .equ CounterGroup+09 ; MirrorC .equ CounterGroup+010 ; MirrorD .equ CounterGroup+011 ; back translation LSB COUNT1H .equ CounterGroup+012 ; received count COUNT1L .equ CounterGroup+013 COUNT3H .equ CounterGroup+014 COUNT3L .equ CounterGroup+015 loopcount .equ r3 ; countera .equ r4 ; counterb .equ r5 ; counterc .equ r6 ; counterd .equ r7 ; mirrora .equ r8 ; mirrorb .equ r9 ; mirrorc .equ r10 ; mirrord .equ r11 ; Radio2Group .equ 080H PREVFIX .equ Radio2Group + 0 PREVTMP .equ Radio2Group + 1 ROLLBIT .equ Radio2Group + 2 RTimeDH .equ Radio2Group + 3 RTimeDL .equ Radio2Group + 4 RTimePH .equ Radio2Group + 5 RTimePL .equ Radio2Group + 6 ID₁₃ B .equ Radio2Group + 7 SW_B .equ Radio2Group + 8 RADIOBIT .equ Radio2Group + 9 RadioTimeOut .equ Radio2Group + 10 RadioMode .equ Radio2Group + 11 ;Fixed or rolling mode BitThresh .equ Radio2Group + 12 ;Bit decision threshold SyncThresh .equ Radio2Group + 13 ;Sync pulse decision threshold MaxBits .equ Radio2Group + 14 ;Maximum number of bits RFlag .equ Radio2Group + 15 ;Radio flags prevfix .equ r0 prevtmp .equ r1 rollbit .equ r2 id_b .equ r7 sw_b .equ r8 radiobit .equ r9 radiotimeout .equ r10 radiomode .equ r11 rflag .equ r15 OriginalGroup .equ 90H SW_DATA .equ OrginalGroup+0 ONEP2 .equ OrginalGroup+1 ; 1.2 SEC TIMER TICK .125 LAST_CMD .equ OrginalGroup+2 ; LAST COMMAND FROM ; = 55 WALL CONTROL ; = 00 RADIO CodeFlag .equ OrginalGroup+3 ; Radio code type flag ; FF = Learning open/close/stop ; 77 = b code ; AA = open/close/stop code ; 55 = Light control trasnmitter ; 00 = Command or unknown RPMONES .equ OrginalGroup+4 ; RPM Pulse One Sec. Disable RPMCLEAR .equ OrginalGroup+5 ; RPM PULSE CLEAR & TEST TIMEP FAREVFLAG .equ OrginalGroup+6 ; RPM FORCED AREV FLAG ; 88H FOR A FORCED REVERSE FLASH_FLAG .equ OrginalGroup+7 FLASH_DELAY .equ OrginalGroup+8 REASON .equ OrginalGroup-30 9 FLASH_COUNTER .equ OrginalGroup+10 RadioTypes .equ OrginalGroup+11 ; Types for one page of tx's LIGHT_FLAG .equ OrginalGroup+12 CMD_DEB .equ OrginalGroup+13 LIGHT_DEB .equ OrginalGroup+14 VAC_DEB .equ OrginalGroup+15 NextGroup .equ 0A0H SDISABLE .equ NextGroup+0 ; system disable timer PRADIO3H .equ NextGroup+1 ; 3 mS code storage high byte PRADIO3L .equ NextGroup+2 ; 3 mS code storage low byte PRADIO1H .equ NextGroup+3 ; 1 mS code storage high byte PRADIO1L .equ NextGroup+4 ; 1 mS code storage low byte RTO .equ NextGroup+5 ; radio time out ;RFlag .equ NextGroup+6 ; radio flags EnableWorkLight .equ NextGroup+6 ; 4-22-97 work light function on or off? RINFILTER .equ NextGroup+7 ; radio input filter LIGHT1S .equ NextGroup+8 ; light timer for 1second flash DOG2 .equ NextGroup+9 ; second watchdog FAULTFLAG .equ NextGroup+10 ; flag for fault blink, no rad. blink MOTDEL .equ NextGroup+11 ; motor time delay PPOINT_DEB .equ NextGroup+12 ; Pass Point debouncer DELAYC .equ NextGroup+13 ; for the time delay for command L_A_C .equ NextGroup+14 ; Limits are changing register CMP .equ NextGroup+15 ; Counter compare result BACKUP_GRP .equ 0B0H PCounterA .equ BACKUP_GRP PCounterB .equ BACKUP_GRP+1 PCounterC .equ BACKUP_GRP+2 PCounterD .equ BACKUP_GRP+3 HOUR_TIMER .equ BACKUP_GRP+4 HOUR_TIMES_HI .equ BACKUP_GRP+4 HOUR_TIMER_LO .equ BACKUP_GRP+5 PassCounter .equ BACKUP_GRP+6 STACKREASON .equ BACKUP_GRP+7 FirstRun .equ BACKUP_GRP+8 ; Flag for first operation after power-up MinSpeed .equ BACKUP_GRP+9 BRPM_COUNT .equ BACKUP_GRP+10 BRPM_TIME_OUT .equ BACKUP_GRP+11 BFORCE_IGNORE .equ BACKUP_GRP+12 BAUTC_DELAY .equ BACKUP_GRP+13 BCMD_DEB .equ BACKUP_GRP+14 BSTATE .equ BACKUP_GRP+15 ; Double-mapped registers for M6800 test COUNT_HI .equ BRPM_COUNT COUNT_LO .equ BRPM_TIME_OUT COUNT .equ BFORCE_IGNORE REGTEMP .equ BAUTO_DELAY REGTEMP2 .equ BCM2_DEB ; Double-mapped registers for Siminor Code Reception CodeT0 .equ COUNT1L ; Binary radio code received CodeT1 .equ Radio1L CodeT2 .equ MirrorC CodeT3 .equ MirrorD CodeT4 .equ COUNT3H CodeT5 .equ COUNT3L Ix .equ COUNT1H ; Index per Siminor's code W1High .equ AddValueH ; Word 1 per Siminor's code W1Low .equ AddValueL ; description w1high .equ addvalueh w1low .equ addvaluel W2High .equ Radio3H ; Word 2 per Siminor's code W2Low .equ Radio3L ; description w2high .equ radio3h w2low .equ radio3l STACKTOP .equ 238 ; start of the stack STACKEND .equ 0C0H ; end of the stack RS2321P .equ P0 ; RS232 input port RS232IM .equ SWITCHES1 ; RS232 mask csh .equ 10000000B ; chip select high for the 93c46 csl .equ ˜csh ; chip select low for 93c46 clockh .equ 01000000B ; clock high for 93c46 clockl .equ ˜clockh ; clock low for 93c46 doh .equ 00100000B ; data out high for 93c46 dol .equ ˜doh ; data out low for 93c46 ledh .equ 00000010B ; turn the led pin high “off ledl .equ ˜ledh ;turn the led pin low “on psmask .equ 01000000B ; mask for the program switch csport .equ P2 ; chip select port dioport .equ P2 ; data i/o port clkport .equ P2 ; clock port ledport .equ P2 ; led port psport .equ P2 ; program switch port WATCHDOG_GROUP .equ 0FH pcon .equ r0 smr .equ r11 wdtmr .equ r15 ; .IF TwoThirtyThree ; ;WDT .macro ; .byte 5fH ; .endm ; ; .ELSE ; ;MDT .macro ; xcr F1, #00101101b ; Kick external watchdog ; .endm ; ; .ENDIF FILL .macro .byte 0ffh .endm FILL10 .macro FILL FILL FILL FILL FILL FILL FILL FILL FILL FILL .endm FILL100 .macro FILL10 FILL10 FILL10 FILL10 FILL10 FILL10 FILL10 FILL10 FILL10 .endm FILL1000 .macro FILL100 FILL100 FILL100 FILL100 FILL100 FILL100 FILL100 FILL100 FILL100 FILL100 .endm TRAP .macro jp start jp start jp start jp start jp start .endm TRAP10 .macro TRAP TRAP TRAP TRAP TRAP TRAP TRAP TRAP TRAP TRAP .endm SetRpToRadio2Group .macro .byte 031H .byte 08CH .endm Interrupt Vector Table .org 0000H .IF TwoThirtyThree .word RADIO_INT ;IRQ0 .word 000CH ;IRQ1, P3.3 .word RPM ;IRQ2, P3.1 .word AUX_OBS ;IRQ3, P3.0 .word TIMEPVD ;IRQ4, T0 .word RS232 ;IRQ5, T1 .ELSE .word RADIO_INT ;IRQ0 .word RADIO_INT ;IRQ1, P3.3 .word RPM ;IRQ2, P3.1 .word AUX_OBS ;IRQ3, P3.0 .word TIMERUD ;IRQ4, T0 .word 000CH ;IRQ5, T1 .ENDIF .page .org 000CH jp Start ;jmps to start at location 0101, 0202 etc RS232 DATA ROUTINES RS_COUNTER REGISTER: 0000XXXX - 0011XXXX Input byte counter (inputting bytes 1-4) 00XX0000 Waiting for a start bit 00XX0001 -XXXX1001 Input bit counter (Bits 1-9, including stop) 00XX1111 Idle -- whole byte received 1000XXXX - 1111XXXX Output byte counter (outputting bytes 1-8) 1XXX0000 Tell the routine to output a byte 1XXX0001 - 1XXX1001 Outputting a byte (Bits 1-9, including stop) 1XXX1111 Idle -- whole byte output OutputMode: tm RS_COUNTER, #00001111B ; Check for outputting start bit jr z, OutputStart ; tcm RS_COUNTER, #00001001B ; Check for outputting stop bit jr z, OutputStop ; (bit 9), if so, don't increment OutputData: scf ; Set carry to ensure high stop bit rrc RS232DAT ;Test the bit for output jr c, OutputHigh ; OutputLow: and p3, *˜CHAPSE_SW ; Turn off the pull-up cr P3, *DIS_SW ; Turn on the pull-down jr DataBitDone ; OutputStart: ld T1,#RsPerFull ; Set the timer to a full bit period ld TMR, #00001110B ; Load the full time period and p3, #˜CHARGE_SW ; Send a start bit or P3, #DIS_SW ; inc RS_COUNTER ; Set the counter to first bit iret ; OutputHigh: and p3, #˜DIS_SW ; Turn off the pull-down or P3, #CHARGE_SW ; Turn on the pull-up DataBitDone: inc RS_COUNTER ; Advance to the next data bit iret ; OutputStop: and p3, #˜DIS_SW ; Output a stop (high) bit or P3, #CHARGE_SW ; or RS_COUNTER, #00001111B ; Set the flag for word being done cp RS_COUNTER, #11111111B ; Test for last output byte jr nz, MoreOutput ; If not, wait for more output clr RS_COUNTER ; Start waiting for input bytes MoreOutput: RSExit: iret ; RS232: cp RsMode, #00 ; Check for in RS232 mode, jr nz, InRsMode ; If so, keep receiving data cp STATUS, #CHARGE ; Else, only receive data when jr nz, WallModeBad ; charging the wall control InRsMode: tcm RS_COUNTER, #00001111B ; Test for idle state jr z, RSExit ; If so, don't do anything tm RS_COUNTER, #11000000B ; test for input or output mode jr nz, OutputMode RSInput: tm RS_COUNTER, #00001111B ; Check for waiting for start jr z, WaitForStart ; If so, test for start bit tcm RS_COUNTER, #00001001B ; Test for receiving the stop bit jr z, StopBit ; If so, end the word scf ; Initially set the data in bit tm RS232IP, #RS232IM ; Check for high or low bit at input jr nz, GotRsBit ; If high, leave carry high rcf ; Input bit was low GotRsBit: rrc RS232DAT ; Shift the bit into the byte inc RS_COUNTER ; Advance to the next bit iret Stopbit: tm RS232IP,#RS232IM ; Test for a valid stop bit jr z, DataBad ; If invalid, throw out the word DataGood: tm RS_COUNTER, #11110000B ; If we're not reading the first word, jr nz, IsData ; then this is not a command ld RSCOMMAND, RS232DAT ; Load the new command word IsData: or RS_COUNTER, #00001111B ; Indicate idle at end of word iret WallModeBad: clr RS_COUNTER ; Reset the RS232 state DataBad: and RS_COUNTER, #00110000B ; Clear the byte counter iret WaitForStart: tm RS232IP,#RS232IM ; Check for a start bit jr nz, NoStartBit ; If high, keep waiting inc RS_COUNTER ; Set to receive bit 1 ld T1, #RsPer1P22 ; Long time until next sample ld TMR, #00001110B ; Load the timer ld T1, #RsPerFull ; Sample at 1X afterwords iret ; NoStartBit: ld T1, #RsPerHalf ; Sample at 2X for start bit iret Set the worklight timer to 4.5 minutes for 60Hz line and 2.5 minutes for 50 Hz line SetVarLight: cp LinePer, #36 ; Test for 50Hz or 60Hz jr uge, EuroLight ; Load the proper table USALight: ld LIGHT_TIMER_HI,#USA_LIGHT_HI ; set the light period ld LIGHT_TIMER_LO,#USA_LIGHT_LO ; ret ; Return EuroLight: ld LIGHT_TIMER_HI,#EURO_LIGHT_HI ; set the light period ld LIGHT_TIMER_LO,#EURO_LIGHT_LO ; ret ; Return THIS THE AUXILARY OBSTRUCTION INTERRUPT ROUTINE AUX_OBS: ld OBS_COUNT, #11 ; reset pulse counter (no obstruction) and imr,#11110111b ; turn off the interupt for up to 500uS ld AOBSTEST,#11 ; reset the test timer or AOBSF,#00000010B ; set the flag for got a aobs and AOBSF,#11011111B ; Clear the bad aobs flag iret ; return from int Test for the presence of a blinker module LookForFlasher: and P2M_SHADOW, #˜BLINK_PIN ;Set high for autolatch test ld P2M, P2M_SHADOW ; or P2, #BLINK_PIN ; or P2M_SHADOW, #BLINK_PIN ;Look for Flasher module ld P2M, P2M_SHADOW ; ret ; Full 41 bytes of unused memory FILL10 FILL10 FILL10 FILL10 FILL REGISTER INITILISATION .org 0011H ; address has both bytes the same start: START: di ; turn off the interrupt for init .IF TwoThirtyThree ld RP,#WATCHDOG_GROUP ld wdtmr,#00001111B ; rc dog 100mS .ELSE clr p1 .ENDIF WDT ; kick the dog clr RP ; clear the register pointer PORT INITILIZATION ld p0,#P01S_INIT ; RESET all ports ld P2,#P2S_POR ; Output the chip ID code ld P3,#P3S_INIT ; ld P01M,#P01M_INIT ; set mode p00-p03 out p04-p07in ld P3M,#P3M_INIT ; set port3 p30-p33 input analog mode ; p34-p37 outputs ld P2M,#P2M_POR ; set port 2 mode for chip ID out Internal RAM Test and Reset All RAM = mS srp #0F0h ; point to control group use stack ld r15,#4 ;r15= pointer (minimum of RAM) write_again: WDT ; KICK THE DOG ld r14,#1 write_again1: ld @r15,r14 ;write 1,2,4,8,10,20,40,80 cp r14,@r15 ;then compare jr ne,system_error rl r14 jr nc,write_again1 clr Εr15 ;write RAM(r5)=0 to memory inc r15 cp r15,#240 jr ult,write_again Checksum Test CHECKSUMTEST: srp #CHECK_GRP ld test_adr_hi,#01FH ld test_adr_lo,#0FFH ;maximum address=fffh add_sum: WDT ; KICK THE DOG idc rom_data,@test_adr ;read ROM code one by one add checkp_sum,rom_dat ;add it to checksum register decw test_addr ;increment ROM address jr nz,add_sum ;address=0 ? cp check_sum,#check_sum_value jr z,system_ok ;check final checksum = 00 ? system_error: and ledport,#ledl ; turn on the LED to indicate fault jr system_error .byte 256-check_sum_value system_ok WDT ; kick the dog ld STACKEND,#STACKTOP ; start at the top of the stack SETSTACKLOOP: ld @STACKEND,#01H ; set the value for the stack vector dec STACKEND ; next address cp STACKEND,#STACKEND ; test for the last address jr nz,SETSTACKLOOP ; loop till done CLEARDONE: ld STATE,#06 ; set the state to stop ld BSTATE,#06 ; ld OnePass,STATE ; Set the one-shot ld STATUS,#CHARGE ; set start to charge ld SWITCH_DELAY,#CMD_DEL_EX ; set the delay time to cmd ld LIGHT_TIMER_HI,#USA_LIGHT_HI ; set the light period ld LIGHT_TIMER_LO,#USA_LIGHT_LO ; for the 4.5 min timer ld RPMONES,#244 ; set the hold off srp #LEARNEE_GRP ; ld learndb,#0FFH ; set the learn debouncer ld zzwin,learndb ; turn off the learning ld CMD_DEE,learnoo ; in case of shorted switches ld BCMD_DEB,learndb ; in case of shorted switches ld VAC_DEB,learndb ; ld LIGHT_DEB,learndb ; ld ERASET,learndb ; set the erase timer ld learnt,learndb ; set the learn timer ld RTO,learndb ; set the radio time out ld AUXLEARNSW,learndb ; turn off the aux learn switch ld RRTO,learndb ; set the radio timer STACK INITILIZATION clr 254 1D 255,#238 ; set the start of the stack .IF TwoThirtyThree .ELSE clr P1 .ENDIF TIMER INITILIZATION ld pre0,#00000101b ; set the prescaler to /1 for 4MHz ld PRE1,#00010011B ; set the prescaler to /4 for 4MHz clr T0 ; set the counter to count FF through 0 ld T1,#RsPerHalf ; set the period to rs232 period for start bit sample ld TMR,#00001111B ; turn on the timers PORT INITILIZATION lD P0,#P01S_INIT ; RESET all ports ld P2,#P2S_INIT ; ld P3,#P3S_INIT ; ld P01M,#P01M_INIT ; set mode p00-p03 out p04-p07in ld P3M,#P3M_INIT ; set port3 p30-p33 input analog mode ; p34-p37 outputs ld P2M_SHADOW,#P2M_INIT ; Shadow P2M for read ability ld P2M,#P2M_INIT ; set port 2 mode .IF TwoThirtyThree .ELSE clr p1 .ENDIF READ THE MEMORY 2X AND GET THE VACFLAG ld SKIPRADIO,#NOEECOMM ; ld ADDRESS,#VACTIONADDR ; set non vol address to the VAC flag call READMEMORY ; read the value 2X 1X INIT 2ND read call READMEMORY ; read the value ld VACFLAG,MTEMPH ; save into volital WakeUpLimits: ld ADDRESS, #UPLIMADDR ; Read the up and down limits into memory call READMEMORY ; ld UP_LIMIT_HI, MTEMPH ; ld UP_LIMIT_LO, MTEMPL ; ld ADDRESS, #DNLIMADDR ; call READMEMORY ; ld DN_LIMIT_HI, MTEMPH ; ld DN_LIMIT_LO, MTEMPL ; WDT ; Kick the dog WakeUpState: ld ADDRESS, #LASTSTATEADDR ; Read the previous operating state into memory call READMEMORY ; ld STATE, MTEMPL ; Load the state ld PassCounter, MTEMPH ; Load the pass point couter cp STATE, #UP_POSITION ; If at up limit, set position jr z, WakeUpLimit ; cp STATE, #DN_POSITION ; If at down limit, set position jr z, WakeOnLimit ; WakeUpLost: ld STATE, #STOP ; Set state as stopped in mid travel ld POSITION_HI, #07FH ; Set position as lost ld POSITION_LO, #080H ; jr GotWakeUp ; WakeUpLimit: ld POSITION_HI, UP_LIMIT_HI ; Set position as at the up limit ld POSITION_LO, UP_LIMIT_LO ; jr GotWakeUp WakeDnLimit: ld POSITION_HI, DN_LIMIT_HI ; Set position as at the down limit ld POSITION_LO, DN_LIMIT_LO ; GotWakeUp: ld BSTATE, STATE ; Back up the state and ld OnePass, STATE ; clear the one-shot SET ROLLING/FIXED MODE FROM NON-VOLATILE MEMORY call SetRadioMode ; Set the radio mode jr SETINTEPPUPTS ; Continue on SetRadioMode: ld SKIPRADIO, #NCEECOMM ; Set skip radio flag ld ADDRESS, #MODEADDR ; Point to the radio mode flag call READMEMORY ; REad the radio mode ld RadioMode, MTEMPL ; Set the proper radio mode clr SKIPRADIO ; Re-enable the radio tm RadioMode, #ROLL_MASK ; Do we want rolling numbers jr nz, StartRoll call FixedNums ret StartRoll: call RollNums ret INITERRUPT INITILIZATION SETINTERRUPTS: ld IPR,#00011010B ; set the priority to timer ld IMR,#ALL_ON_IMR ; turn on the interrupt .IF TwoThirtyThree ld IRQ,#01000000B ; set the edge clear int .ELSE ld IRQ,#00000000b ; Set the edge, clear ints .ENDIF ei ; enable interrupt RESET SYSTEM REG .IF TwoThirtyThree ld RP,#WATCHDOG_GROUP ld smr,#00100010B ; reset the xtal / number ld pcon,#01111110B ; reset the pcon no comparator output ; no low emi mode clr RP ; Reset the RP .ENDIF ld PRE0,#00000101B ; set the prescaler to / 1 for 4Mhz WDT ; Kick the dog MAIN LOOP MAINLOOP: cp PrevPass, PassCounter ;Compare pass point counter to backup jr z, PassPointCurrent ;If equal, EEPROM is up to date PassPointChanged: ld SKIPRADIO, #NOEECOMM ; Disable radio EEPROM communications ld ADDRESS, #LASTSTATEADDR ; Point to the pass point storage call READMEMORY ; Get the current GDO state di ; Lock in the pass point state ld MTEMPH, PassCounter ; Store the current pass point state ld PrevPass, PassCounter ; Clear the one-shot ei : call WRITEMEMORY ; Write it back to the EEPROX clr SKIPRADIO PassPointCurrent: ; ;4-22-97 CP EnableWorkLight, #10000000B ;is the debouncer set? if so write and ; give feedback JR NE,LightOpen TM p0,#LIGHT_ON JR NZ,GetRidOfIt LD MTEMPL,#0FFH ;turn on the IR beam work light function LD MTEMPH,#0FFH JR CommitToMem GetRidOfIt: LD MTEMPL,#00H ;turn off the IR beam work light function LD MTEMPH,#00H CommitToMem: LD SKIPRADIO,#NOEECOMM ;write to memory to store if enabled or not LD ADDRESS,#IRLIGHTADDR ;set address for write CALL WRITEMEMORY CLR SKIPRADIO XOR p0,#WORKLIGHT ; toggle current state of work light for feedback LD EnableWorklight,#01100000B LightOpen: cp LIGHT_TIMER_HI,#0FFH ; if oight timer not done test beam break jr nz,TestBeamBreak tm p0,#LIGHT_ON ; if the light is off test beam break jr nz,LightSkip TestBeamBreak: tm AOBSF,#10000000b ; Test for broken beam jr z,LightSkip ; if no pulses Staying blocked ; else we are intermittent ;4-22-97 LD SKIPRADIO,#NOEECOMM ;Turn off radio interrupt to read from e2 LD ADDRESS,#IRLIGHTADDR ; CALL READMEMORY CLR SKIPRADIO ; don't forget to zero the one shot CP MTEMPL,#DISABLED ;Does e2 report that IR work light function EQ,LightSkip ;is Disabled? If so jump over light on and CP STATE,#2 ; test for the up limit JR nz,LightSkip ; if not goto output the code call SetVarLight ; Set worklight to proper time or p0,#LIGHT_ON ; turn on the light LightSkip: ;4-22-97 AND AOBSF,#01111111B ;Clear the one shot,for IR beam ;break detect. cp HOUR_TIMER_HI, #010H ; If and hour has passed, jr ult, NoDecrement ; then decrement the cp HOUR_TIMER_LO, #020H ; temporary password timer jr ult, NoDecrement ; clr HOUR_TIMER_HI ; Reset hour timer clr HOUR_TIMER_LO ; ld SKIPRADIO, #NOEECOMM ; Disable radio EE read ld ADDRESS, #DURAT ; Load the temporary password call READMEMORY ; duration from non-volatile cp MTEMPH, #HOURS ; If not in timer mode, jr nz, NoDecrement2 ; then don't update cp MTEMPL, #00 ; If timer is not done, jr z, NoDecrement2 ; decrement it dec MTEMPL ; Update the number of hours call WRITEMEMORY ; NoDecrement: tm AOBSF, #01000000b ; If the poll radio mode flag is jr z, NoDecrement2 ; set, poll the radio mode call SetRadioMode ; Set the radio mode and AOBSF, #10111111b ; Clear the flag NoDecrement2: clr SKIPRADIO ; Re-enable radio reads and AOBSF,#00100011b ; Clear the single break flag clr DOG2 ; clear the second watchdog ld P01M,#P01M_INIT ; set mode p00-p03 out p04-p07in ld P3M,#P3M_INIT ; set port3 p30-p33 input analog mode ; p34-p37 outputs or P2M_SHADOW,#P2M_ALLINS ; Refresh all the P2M pins which have are and P2M_SHADOW,#P2M_ALLOTS ; always the same when we get here ld P2M,P2M_SHADOW ; set port 2 mode cp VACCHANGE,#0AAH ; test for the vacation change flag jr nz,NOVACCHG ; if no change the skip cp VACFLAG,#0FFH ; test for in vacation jr z,MCLEARVAC ; if in vac clear ld VACFLAG,#0FFH ; set vacation jr SETVACCHANGE ; set the change MCLEARVAC: clr VACFLAG ; clear vacation mode SETVACCHANGE: clr VACCHANGE ; one shot ld SKIPRADIO,#NOEECOMM ; set skip flag ld ADDRESS,#VACATIONADDR ; set the non vol address to the VAC flag ld MTEMPH,VACFLAG ; store the vacation flag ld MTEMPL,VACFLAG ; call WRITEMEMORY ; write the value clr SKIPRADIO ; clear skip flag NOVACCHG: cp STACKFLAG,#0FFH ; test for the change flag jr nz,NOCHANGEST ; if no change skip updating cp L_A_C, #070H ; If we're in learn mode jr uge, SkipReadLimits ; then don't refresh the limits! cp STATE, #UP_DIRECTION ; If we are going to travel up jr z, ReadUpLimit ; then read the up limit cp STATE, #DN_DIRECTION ; If we are going to travel down jr z, ReadDnLimit ; then read the down limit jr SkipReadLimits ; No limit on this travel . . . ReadUpLimit: ld SKIPRADIO, #NOEECOMM ; Skip radio EEPROM reads ld ADDRESS, #UPLIMADDR ; Read the up limit call READMEMORY ; di ; ld UP_LIMIT_HI, MTEMPH ; ld UP_LIMIT_LO, MTEMPL ; clr FirstRun ; Calculate the highest possible value for pass count add MTEMPL, #10 ; Bias back by 1” to provide margin of error adc MTEMPH, #00 ; CalcMaxLoop: inc FirstRun ; add MTEMPL, #LOW(PPOINTPULSES) ; adc MTEMPH, #HIGH(PPOINTPULSES) ; jr nc, CalcMaxLoop ; Count pass points until value goes positive GotMaxPPoint: ei ; clr SKIPRADIO ; tm PassCounter, #01000000b ; Test for a negative pass point counter jr z, CounterGood1 ; If not, no lower bounds check needed cp DN_LIMIT_HI, #HIGHT(PPOINTPULSES - 35) ; If the down limit is low enough, jr ugt, CounterIsNeg1 ; then the counter can be negative jr ult, ClearCount ; Else, it should be zero cp DN_LIMIT_LO, #LOW(PPOINTPULSES - 35) jr uge, CounterIsNeg1 ; ClearCount: and PassCounter, #10000000b ; Reset the pass point counter to zero jr CounterGood1 ; CounterIsNeg1: or PassCounter, #01111111b ; Set the pass point counter to −1 CounterGood1: cp UP_LIMIT_HI, #0FFH ; Test to make sure up limit is at a jr nz, TestUpLimit2 ; a learned and legal value cp UP_LIMIT_LO, #0FFH ; jr z, LimitIsBad ; jr LimitsAreDone ; TestUpLimit2: cp UP_LIMIT_HI, #0D0H ; Look for up limit set to illegal value jr ule, LimitIsBad ; If so, set the limit fault jr LimitsAreDone ; ReadDnLimit: ld SKIPRADIO, #NOEECOMM ; Skip radio EEPROM reads ld ADDRESS, #DNLIMADDR ; Read the down limit call READMEMORY ; di ; ld DN_LIMIT_HI, MTEMPH ; ld DN_LIMIT_LO, MTEMPH ; ei ; clr SKIPRADIO ; cp DN_LIMIT_HI, #00H ; Test to make sure down limit is at a jr nz, TestDownLimit2 ; a learned and legal value cp DN_LIMIT_LO, #00H ; jr z, LimitIsBad ; jr LimitsAreDone ; TestDownLimit2: cp DN_LIMIT_HI, #020H ; Look for down limit set to illegal value jr ult, LimitsAreDone ; If not, proceed as normal LimitIsBad: ld FAULTCODE, # ; Set the “no limits” fault call SET_STOP_STATE ; Stop the GDO jr LimitsAreDone ; SkipReadLimits: LimitsAreDone: ld SKIPRADIO, #NOEECOMM ; Turn off the radio read ld ADDRESS, #LASTSTATEADDR ; Write the current state and pass count call READMEMORY ; ld MTEMPH, PassCounter ; DON'T update the pass point here! ld MTEMPL, STATE ; call WRITEMEMORY ; clr SKIPRADIO ; ld OnePass, STATE ; Clear the one-shot cp L_A_C, #077H ; Test for successful learn cycle jr nz, DontWriteLimits ; If not, skip writing limits WriteNewLimits: cp STATE, #STOP ; jr nz, WriteUpLimit ; cp LIM_TEST_HI, #00 ; Test for (force) stop witin 0.5″ of jr nz, WriteUpLimit ; the original up limit position cp LIM_TEST_LO, #16 ; jr ugt, WriteUpLimit ; BackOffUpLimit: ; add UP_LIMIT_LO, # ; Back off the up limit by 0.5″ add UP_LIMIT_HI, #00 ; WriteUpLimit: ld SKIPRADIO, #NOEECOMM ; Skip radio EEPROM reads ld ADDRESS, #UPLIMADDR ; Read the up limit di ; ld MTEMPH, UP_LIMIT_HI ; ld MTEMPL, UP_LIMIT_LO ; ei ; call WRITEMEMORY ; WriteDnLimit: ld ADDRESS, #DNLIMADDR ; Read the up limit di ; ld MTEMPH, DN_LIMIT_HI ; ld MTEMPL, DN_LIMIT_LO ; ei ; call WRITEMEMORY ; WritePassCount: ld ADDRESS, #LASTSTATEADDR ; Write the current state and pass count ld MTEMPH, PassCounter ; Update the pass point ld MTEMPL, STATE ; call WRITEMEMORY ; clr SKIPRADIO ; clr L_A_C ; Leave the learn mode or ledport,#ledh ; turn off the LED for program mode DontWriteLimits: srp #LEARNEE_GRP ; set the register pointer clr STACKFLAG ; clear the flag ld SKIPRADIO, #NOEECOMM ; set skip flag ld address,#CYCCOUNT ; set the non vol address to the cycle c call READMEMORY ; read the value inc mtemp1 ; increase the counter lower byte jr nz,COUNTER1DONE ; inc mtemph ; increase the counter high byte jr nz,COUNTER2DONE ; call WRITEMEMORY ; store the value inc address ; get the next bytes call READMEMORY ; read the data inc mtemp1 ; increase the counter low byte jr nz,COUNTER2DONE ; inc mtemph ; increase the vounter high byte COUNTER2DONE: call WRITEMEMORY ; save the value ld address,#CYCCOUNT ; call READMEMORY ; read the data and mtemph,#00001111B ; find the force address or mtemph,#30H ; ld ADDRESS,MTEMPH ; set the address ld mtemp1,DNFORCE ; read the forces ld mtemph,UPFORCE ; call WRITEMEMORY ; write the value jr CDONE ; done set the back trace COUNTER1DONE: call WRITEMEMORY ; got the new address CDONE: clr SKIPRADIO ; clear skip flag NOCHANGEST: call LEARN ; do the learn switch di cp BRPM_COUNT_RPM_COUNT jr z,TESTRPM RESET: jp START TESTRPM: cp BRPM_TIME_OUT,RPM_TIME_OUT jr nz,RESET cp BFORCE_IGNORE,FORCE_IGNORE jr nz,RESET ei di cp BAUTO_DELAY,AUTO_DELAY jr nz,REESET cp BCMD_DEB,CMD_DEB jr nz,RESET cp BSTATE,STATE jr nz,RESET ei TESTRS232: SRP #TIMER_GROUP tcm RS_COUNTER, #00001111B ; If we are at the end of a word, jp nz, SIPRS232 ; then handle the RS232 word cp rscommand,#‘V’ ; jp ugt,ClearRS232 ; cp rscommand,#‘0’ ; test for in range jp ult,ClearRS232 ; if out of range skip cp rscommand,#‘<’ ; If we are reading jr nz,NotRs3C ; go straight there call GotRs3C ; jp SIPRS232 ; NotRs3C: cp rscommand,#‘>’ ; If we are writing EEPROM jr nz,NotRs3E ; go straight there call GotRs3E ; jp SKIPRS232 ; NotRs3E: ld rs_temp_hi,#HIGH (RS232JumpTable-(3*‘0’)) ; address pointer to table ld rs_temp_lo,#LOW (RS232JumpTable-(3*‘0’)) ; Offset for ASCII adjust add rs_temp_lo,rscommand ; look up the jump 3x adc rs_temp_hi,#00 ; add rs_temp_lo,rscommand ; look up the jump 3x adc rs_temp_hi,·00 ; add rs_temp_lo,rscommand ; look up the jump 3x adc rs_temp_hi,#00 ; call @rs_temp ; call this address jp SKIPRS232 ; done RS232JumpTable: jp GotRs30 jp GotRs31 jp GotRs32 jp GotRs33 jp GotRs34 jp GotRs35 jp GotRs36 jp GotRs37 jp GotRs38 jp GotRs39 jp GotRs3A jp GotRs3B jp GotRs3C jp GotRs3D jp GotRs3E jp GotRs3F jp GotRs40 jp GotRs41 jp GotRs42 jp GotRs43 jp GotRs44 jp GotRs45 jp GotRs46 jp GotRs47 jp GotRs48 jp GotRs49 jp GotRs4A jp GotRs4B jp GotRs4C jp GotRs4D jp GotRs4E jp GotRs4F jp GotRs50 jp GotRs51 jp GotRs52 jp GotRs53 jp GotRs54 jp GotRs55 jp GotRs56 ClearRS232: and RS_COUNTER, #11110000b ; Clear the RS232 state SKIPRS232: UpdateForceAndSpeed: ; Update the UP force from the look-up table srp #FORCE_GROUP ; Point ot the proper registers ld force_add_hi, #HIGH(force_table) ; Fetch the proper unscaled ld force_add_lo, #LOW(force_table) ; value from the ROM table di ; add force_add_lo, upforce ; Offset to point to the adc force_add_hi, #00 ; proper place in the table add force_add_lo, upforce ; x2 adc force_add_hi, #00 ; add force_add_lo, upforce ; x3 (three bytes wide) adc force_add_hi, #00 ; ei ; ldc force_temp_of, @force_add ; Fetch the ROM bytes incw force_add ; ldc force_temp_hi, @force_add ; incw force_add ; ldc force_temp_lo, @force_add ; ld Divisor, PowerLevel ; Divide by our current force level call ScaleTheSpeed ; Scale to get our proper force number di ; Update the force registers ld UP_FORCE_HI, force_temp_hi ; ld UP_FORCE_LO, force_temp_lo ; ei ; ; Update the DOWN force from the look-up table ld force_add_hi, #HIGH(force_table) ; Fetch the proper unscaled ld force_add_lo, #LOW(force_table) value from the ROM table di ; add force_add_lo, dnforce ; Offset to point to the adc force_add_hi, #00 ; proper place in the table add force_add_lo, dnforce ; x2 adc force_add_hi, #00 ; add force_add_lo, dnforce ; x3 (three bytes wide) adc force_add_hi, #00 ; ei ; ldc force_temp_of, @force_add ; Fetch the ROM bytes incw force_add ; ldc force_temp_hi, @force_add ; incw force_add ; ldc force_temp_lo, @force_add ; ld Divisor, PowerLevel ; Divide by our current force level call ScaleTheSpeed ; Scale to get our proper force number di ; Update the force registers ld DN_FORCE_HI, force_temp_hi ; ld DN_FORCE_LO, force_temp_lo ; ei ; ; Scale the minumum speed based on force setting cp STATE, #DN_DIRECTION ; If we're traveling down, jr z, SetDownMinSpeed ; then use the down force pot for min. speed SetUpMinSpeed: di ; Disable interrupts during update ld MinSpeed, UPFORCE ; Scale up force pot jr MinSpeedMath ; SetDownMinSpeed: di ; ld MinSpeed, DNFORCE ; Scale down force pot MinSpeedMath: sub MinSpeed, #24 ; pot level - 24 jr nc, UpStep2 ; truncate off the negative number clr MinSpeed ; UpStep2: rcf ; Divide by four rrc MinSpeed ; rcf ; rrc MinSpeed ; add MinSpeed, #4 ; Add four to find the minimum speed cp MinSpeed, #12 ; Perform bounds check on minium speed jr ule, MinSpeedOkay ; Truncate if necessary ld MinSpeed, #12 ; MinSpeedOkay: ei ; Re-enable interrupts ; Make sure the worklight is at the proper time on power-up cp LinePer, #36 ; Test for a 50 Hz system jr ult, TestRadioDeadTime ; if not, we don't have a problem cp LIGHT_TIMER_HI, #0FFH ; If the light timer is running jr z, TestRadioDeadTime ; and it is greater than cp LIGHT_TIMER_HI, #EURO_LIGHT_HI ; the European time, fix it jr ule, TestRadioDeadTime ; call SetVarLight ; TestRadioDeadTime: cp R_DEAD_TIME, #25 ; test for too long dead jp nz,MAINLOOP ; if not loop clr RadioC ; clear the radio counter clr RFlag ; clear the radio flag jp MAINLOOP ; loop forever Speed scaling (i.e. Division) routine ScaleTheSpeed: clr TestReg ld loopreg, #24 ; Loop for all 24 bits DivideLoop: rcf ; Rotate the next bit into rlc force_temp_lo ; the test field rlc force_temp_hi ; rlc force_temp_of ; rlc TestReg ; cp TestReg, Divisor ; Test to see if we can subtract jr ult, BitIsDone ; If we can't, we're all done sub TestReg, Divisor ; Subtract the divisor or force_temp_lo, #00000001b ; Set the LSB to mark the subtract BitIsDone: djnz loopreg, DivideLoop ; Loop for all bits DivideDone: ; Make sure the result is under our 500 ms limit cp force_temp_of, #00 ; Overflow byte must be zero jr nz, ScaleDown ; cp force_temp_hi, #0F4H ; jr ugt,ScaleDown ; jr ult, DivideIsGood ; If we're less, then we're okay cp force_temp_lo, #024H ; Test low byte jr ugt,ScaleDown ; if low byte is okay, DivideIsGood: ret ; Number is good ScaleDown: ld force_temp_hi, #0F4H ; Overflow is never used anyway ld force_temp_lo, #024H ; ret RS232 SUBROUTINES “0” Set Command Switch GotRs30: ld LAST_CMD,#0AaH ; set the last command as rs wall cmd call CmdSet ; set the command switch jp NoPos “1” Clear Command Switch GotRs31: call CmdRel ; release the command switch jp NoPos “2” Set Worklight switch GotRs32: call LightSet ; set the light switch jp NoPos “3” Clear Worklight Switch GotRs33: clr LIGHT_DEB ; Release the light switch jp NoPos “4” Set Vacation Switch GotRs34: call VacSet ; Set the vacation switch jp NoPos “5” Clear Vacation Switch GotRs35: clr VAC_DEB ; release the vacation switch jp NoPos “6” Set smart switch GotRs36: call SmartSet jp NoPos “7” Clear Smart switch set GotRs37: call SmartRelease jp NoPos “8” Return Present state and reason for that state GotRs38: ld RS232DAT,STATE or RS232DAT,STACKREASON jp LastPos “9” Return Force Adder and Fault GotRs39: ld RS232DAT,FAULTCODE ; insert the fault code jp LastPos “:” Status Bits GotRs3A: clr RS232DAT ; Reset data tm P2, #01000000b ; Check the strap jr z, LookForBlink ; If none, next check or RS232DAT, #00000001b ; Set flag for strap high LookForBlink: call LookForFlasher ; tm P2, #BLINK_PIN ; If flasheer is present, jr nz, ReadLight ; or RS232DAT,#00000010b ; then idicate it ReadLight: tm P0,#00000010B ; read the light jr z,C3ADone or RS232DAT,#00000100b C3ADone: cp CodeFlag, #REGLEARN ; Test for being in a learn mode jr ult, LookForPass ; If so, set the bit or RS232DAT,#00010000b ; or RS232DAT,#000100000b ; LookForPass: tm PassCounter,#01111111b ; Check for above pass point jr z, LookForProt ; If so, set the bit tcm PassCounter,#01111111b ; jr z, LookForProt ; or RS232DAT,#00100000b ; LookForProt: tm ACBSF, #10000000b ; Check for protector break/block jr nz, LookForVac ; If blocked, don't set the flag or RS232DAT,#01000000b ; Set flag for protector signal good LookForVac: cp VACFLAG,#00B ; test for the vacation mode jp nz,LastPos or RS232DAT,#00001000b jp LastPos “;” Return L_A_C GotRs3B: ld RS232DAT,L_A_C ; read the L_A_C jr LastPos “<” Read a word of data from an EEPROM address input by the user GotRs3C: cp RS_COUNTER, #010H ; If we have only received the jr ult, FirstByte ; first word, wait for more cp RS_COUNTER, #080H ; If we are outputting, jr ugt, OutputSecond ; output the second byte SecondByte: ld SKIPRADIO, #0FFH ; Read the memory at the specified ld ADDRESS, RS232DAT ; address call READMEMORY ; ld RS232DAT, MTEMPH ; Store into temporary registers ld RS_TEMP_LO, MTEMPL ; clr SKIPRADIO ; jp MidPos ; OutputSecond: ld RS232DAT, RS_TEMP_LO ; Output the second byte of the read jp LastPos ; FirstByte: inc RS_COUNTER ; Set to receive second word ret ; “=” Exit learn limits mode GotRs3D: cp L_A_C, #00 ; If not in learn mode, jp z, NoPos ; then don't touch the learn LED clr L_A_C ; Reset the learn limits state machine or leaport,#ledn ; turn off the LED for program mode or ledport,#ledh ; turn off the LED for program mode jp NoPos ; “>” Write a word of dat ato the address input by the user GotRs3E: cp RS_COUNTER, #01FH ; jr z, SecondByteW ; cp RS_COUNTER, #02FH ; jr z, ThirdByteW ; cp RS_COUNTER, #03FH ; jr z, FourthByteW ; FirstByteW: DataDone: inc RS_COUNTER ; Set to receive next byte ret SecondByteW: ld RS_TEMP_HI, RS232DAT ; Store the address jr DataDone ; ThirdByteW: ld RS_TEMP_LO, RS232DAT ; Store the high byte jr DataDone ; FourthByteW: cp RS_TEMP_HI, #03FH ; Test for illegal address jr ugt, FailedWrite ; If so, don't write ld SKIPRADIO, #0FFH ; Turn off radio reads ld ADDRESS, RS_TEMP_HI ; Load the address ld MTEMPH, RS_TEMP_LO ; and the data for the ld MTEMPL, RS232DAT ; EEPROM write call WRITEMEMORY ; clr SKIPRADIO ; Re-enable radio reads ld RS232DAT, #00H ; Flag write okay jp LastPos ; FailedWrite: ld RS232DAT, #0FFH ; Flag bad write jp LastPos “?” ; Suspend all communication for 30 seconds GotRs3F: clr RSCOMMAND ; Throw out any command currently ;running jp NoPos ; Ignore all RS232 data “@” ; Force Up State GotRs40: cp STATE, #DN_DIRECTION ; If traveling down, make sure that jr z, dontup ; the door autoreverses first cp STATE, #AUTO_REV ; If the door is autoreversing or jp z, NoPos ; at the up limit, don't let the cp STATE, #UP_POSITION ; up direction state be set jp z, NoPos ; ld REASON, #00H ; Set the reason as command call SET_UP_DIR_STATE jp NoPos dontup: ld REASON, #00H ; Set the reason as command call SET_AREV_STATE ; Autoreverse the door jp NoPos ; “A” Force Down State GotRs41: cp STATE, #5h ; test for the down position jp z,NoPos ; clr REASON ; Set the reason as command call SET_DN_DIR_STATE jp NoPos “B” Force Stop State GotRs42: clr REASON ; Set the reason as command call SET_STOP_STATE jp NoPos “C” Force Up Limit State GotRs43: clr REASON ; Set the reason as command call SET_UP_POS_STATE jp NoPos “D” Force Down Limit State GotRs44: clr REASON ; Set the reason as command call SET_DN_POS_STATE jp NoPos “E” Return min. force during travel GotRs45: ld RS232DAT,MIN_RPM_HI ; Return high and low cp RS_COUNTER,#090h ; bytes of min. force read jp ult,MidPos ; ld RS232DAT,MIN_RPM_LO ; jp LastPos ; “F” Leave RS232 mode -- go back to scanning for wall control switches GotRs46: clr RsMode ; Exit the rs232 mode ld STATUS, #CHARGE ; Scan for switches again clr RS_COUNTER ; Wait for input again ld rscommand,#0FFH ; turn off command ret “G” (No Function) GotRs47: jp NoPos “H” 45 Second search for pass point the setup for the door GotRs48: ld SKIPRADIO, #0FFH ; Disable radio EEPROM reads / writes ld MTEMPH, #0FFH ; Erase the up limit and down limit ld MTEMPL, #0FFH ; in EEPROM memory ld ADDRESS, #UPLIMADDR ; call WRITEMEMORY ; ld ADDRESS, #DNLIMADDR ; call WRITEMEMORY ; ld UP_LIMIT_HI, #HIGH(SetupPos) ; Set the dorr to travel ld UP_LIMIT_LO, #LOW SetupPos) ; to the setup position ld POSITION_HI, #040H ; Set the current position to unknown and PassCounter, #10000000b ; Reset to activate on first pass point seen call SET_UP_DIR_STATE ; Force the door to travel ld OnePass, STATE ; without a limit refresh jp NoPos “I” Return radio drop-out timer GotRs49: clr RS232DAT ; Initially say no radio on cp RTO,#RDROPTIME ; If there's no radio on, jp uge, LastPos ; then broadcast that com RS232DAT ; Set data to FF jp LastPos “J” Return current position GotRs4A: ld RS232DAT,POSITION_HI cp RS_COUNTER,#090H ; Test for no words out yet jp ult, MidPos ; If not, transmit high byte ld RS232DAT,POSITION_LO jp LastPos “K” Set radio Received GotRs4B: cp L_A_C, #070H ; If we were positioning the up limit, jr ult, NormalRSRadio ; then start the learn cycle jr z, FirstRSLearn ; cp L_A_C, #071H ; If we had an error, jp nz, NoPos ; re-learn, otherwise ignore ReLearnRS: ld L_A_C, #072H ; Set the re-learn state call SET_UP_DIR_STATE ; jp NoPos ; FirstRSLearn: ld L_A_C, #073H ; Set the learn state call SET_UP_POS_STATE ; Start from the “up limit” jp NoPos NormalRSRadio: clr LAST_CMD ; mark the last command as radio ld RADIO_CMD,#0AAH ; set the radio command jp NoPos ; return “L” Direct-connect sensitivity test -- toggle worklight for any code GotRs4C: clr RTO ; Reset the drop-out timer ld CodeFlag, #SENS_TEST ; Set the flag to test sensitivity jp NoPos “M” GotRs4D: jp NoPos “N” If we are within the first 4 seconds and RS232 mode is not yet enabled, then echo the nybble on P30 - P33 on all other nybbles (A.K.A. The 6800 test) GotRs4E: cp SDISABLE, #32 ; If the 4 second init timer jp ult, ExitNoTest ; is done, don't do the test di ; Shut down all other GDO operations ld COUNT_HI, #002H ; Set up to loop for 512 iterations, clr COUNT_LO ; totaling 13.056 milliseconds ld P01M, #00000100b ; Set all possible pins or micro. ld P2M, #00000000b ; to outputs for testing ld P3M, #00000001b ; WDT ; Kick the dog TimingLoop: clr REGTEMP ; Create a byte of identical nybbles ld REGTEMP2, P3 ; from P30 - P33 to write to all ports and REGTEMP2, #00001111b ; or REGTEMP, REGTEMP2 ; swap REGTEMP2 ; or REGTEMP, REGTEMP2 ; ld P0, REGTEMP ; Echo the nybble to all ports ld P2, REGTEMP ; ld P3, REGTEMP ; decw COUNT ; Loop for 512 iterations jr nz, TimingLoop ; jp START ; When done, reset the system “O” Return max, force during travel GotRs4F: ld RS232DAT,P32_MAX_HI ; Return high and low cp RS_COUNTER,#090h ; bytes of max. force read jp ult,MidPos ; ld RS232DAT,P32_MAX_LO ; jp LastPos ; “P” Return the measured temperature range GotRs50: jr NoPos “Q” Return address of last memory matching radio code received GotRs51: ld RS232DAT, RTEMP ; Send back the last matching address jr LastPos ; “R” Set Rs232 mode -- No ultra board present Return Version GotRs52: clr UltraBrd ; Clear flag for ultra board present SetIntoRs232: ld RS232DAT,#VERSIONNUM ; Initially return the version cp RsMode,#00 ; If this is the first time we're jr ugt, LockedInNoCR l looking RS232, signal it ld RS232DAT,#0BBH ; Return a flag for initial RS232 lock LockedInNoCR: ld RsMode,#32 jr LastPos “S” Set Rs232 mode -- Ultra board present Return Version GotRs53: jr NoPos “T” Range test -- toggle worklight whenever a good memory-matching code is received GotRs54: clr RTO ; Reset the drop-out timer ld CodeFlag, #RANGETEST ; Set the flag to test sensitivity jr NoPos “U” (No Function) GotRs55: jr NoPos “V” Return current values of up and down force pots GotRs56: ld RS232DAT,UPFORCE ; Return values of up and down cp PS_COUNTER,#090h ; force pots. jp ult,MidPos ; ld RS232DAT,DNFORCE ; jr LastPos MidPos: cr RS_COUNTER, #10000000B ; Set the output mode inc RS_COUNTER ; Transmit the next byte jr RSDone ; exit LastPos: ld RS_COUNTER, #11110000B ; set the start flag for last byte ld rscommand,#0FFH ; Clear the command jr RSDone ; Exit ExitNoTest: NoPos: clr RS_COUNTER ; Wait for input again ld rscommand,#0FFH ; turn off command RSDone: ld RsMode,#32 ; ld STATUS, #RSSTATUS ; Set the wall control to RS232 or P3, #CHARGE_SW ; Turn on the pull-ups and P3, #˜DIS_SW ; ret Radio interrupt from a edge of the radio signal RADIO_INT: push RP ; save the radio pair srp #RadioGroup ; set the register pointer ld rtemph,T0EXT ; read the upper byte ld rtemp1,T0 ; read the lower byte tm IRQ,#00010000B ; test for pending int jr z,RTIMEOK ; if not then ok time tm rtemp1,#10000000B ; test for timer reload jr z,RTIMEOK ; if not reloaded then ok dec rtemph ; if reloaded then dec high for sync RTIMEOK: clr R_DEAD_TIME ; clear the dead time .IF TwoThirtyThree and IMR,#11111110B ; turn off the radio interrupt .ELSE and IMR,#11111100B ; Turn off the radio interrupt .ENDIF ld RTimeDH,PTimePH ; find the difference ld RTimeDL,RTimePL ; sub RTimeDL,rtemp1 ; sbc RTimeDH,rtemph ; in past time and the past time in temp RTIMEDONE: tm P3,#00000100B ; test the port for the edge jr nz,ACTIVETIME ; if it was the active time then branch INACTIVETIME: cp RINFILTER,#0FFH ; test for active last time jr z,GOINACTIVE ; if so continue jp RADIO_EXIT ; if not the return GOINACTIVE: .IF TwoThirtyThree or IRQ,#01000000B ; set the bit setting direction to pos edge .ENDIF clr RINFILTER ; set flag to inactive ld rtimeih,RTimeDH ; transfer difference to inactive ld rtimeil,RTimeDL ; ld RTimePH,rtemph ; transfer temp into the past ld RTimePL,rtempl ; CP radioc,#01H ;inactive time after sync bit JP Z,RADIO_EXIT ;exit if it was not sync TM RadioMode, #ROLL_MASK ;If in fixed mode, JR z, FixedBlank ;no number counter exists CP rtimeih,#0AH ;s.56ms for rolling code mode JP ULT,RADIO_EXIT ;pulse ok exit as normal CLR radioc ;if pulse is longer, bogus sync, restart sync search jp RADIO_EXIT ; return FixedBlank: CP rtimeih,#014H ; test for the max width 5.16ms JP ULT,RADIO_EXIT ;pulse ok exit as normal CLR radioc ;if pulse is longer, bogus sync, restart sync search jp RADIO_EXIT ; return ACTIVETIME: cp RINFILTER,#00H ; test for active last time jr z,GOACTIVE ; if so continue jr RADIO_EXIT ; if not the return GOACTIVE: .IF TwoThirtyThree and IRQ,#00111111B ; clear bit setting direction to neg edge .ENDIF ld RINFILTER,#0FFH ; ld rtimeah,RTimeDH ; transfer difference to active ld rtimeal,RTimeDL ; ld RTimePH,rtemph ; transfer temp into the past ld RTimePL,rtempl ; GotBothEdges: ei ; enable the interrupts cp radioc,#1 ; test for the blank timing jp ugt,INSIG ; if not then in the middle of signal .IF UseSiminor JP z, CheckSiminor ; Test for a Siminor tx on the first bit .ENDIF inc radioc ; set the counter to the next number TM RFlag,#001000000B ;Has a valid blank time occured JR NZ,BlankSkip cp RadioTimeOut,#10 ; test for the min 10 ms blank time jr ult,ClearJump ; if not then clear the radio OP RFlag,#00100000B ;blank time valid! no need to check BlankSkip: cp rtimeah,#00h ; test first the min sync pr z,JustNoise ; if high byte 0 then clear the radio SyncOk: TM RadioMode,#ROLL_MASK ;checking sync pulse with,fix or Roll JR z,Fixedsync CP rtimeah,#09h ;time for roll 1/2 fixed, 2.3ms JR uge,JustNoise JR SET1 Fixedsync: cp rtimeah,#012h ; test of the max time 4.6mS jr uge,JustNoise ; if not clear SET1: clr PREVFIX ;Clear the previous “fixed” bit cp rtimeah, SyncThresh ; test for 1 or three time units jr uge,SYNC3FLAG ; set the sync 3 flag SYNCLFLAG: tm RFlag, #01000000b ;Was a sync 1 word the last received? jr z, SETADCODE ; if not, then this is an A (or D code SETBCCODE: ld radio3h, radio1h ;Store the last sync 1 word ld radio31, radio11 or RFlag, #00000110b ;Set the B/C Code flags and RFlag, #11110111b ;Clear the A/D Code Flag jr BCCODE JustNoise: CLR radioc ;Edge was noise keep waiting for sync bit JP RADIO_EXIT SETADCODE: or RFlag, #00001000b BCCODE: or RFlag,#01000000b ; set the sync 1 memory flag clr radio1h ; clear the memory clr radio11 ; clr COUNT1H ; clear the memory clr COUNT1L ; jr DONESET1 ; do the 2X SYNC3FLAG: and RFlag,#10111111b ; set the sync 3 memory flag clr radio3h ; clear the memory clr radio3l ; clr COUNT3H ; clear the memory clr COUNT3L ; clr ID_B ; Clear the ID bits DONESET1: RADIO_EXIT: and SKIPRADIO, # LOW:˜NOINT) ;Re-enable radio ints pop rp iret ; done return ClearJump: or P2,#10000000b ; turn of the flag bit for clear radio jp ClearRadio ; clear the radio signal .IF UseSiminor SimRadio: tm rtimeah, #10000000b ; Test for inactive greater than active jr nz, SimBitZero ; If so, binary zero received SimBitOne: scf ; Set the bit jr RotateInBit ; SimBitZero: rcf RotateInBit: rrc CodeT0 ; Shift the new bit into the rrc CodeT1 ; radio word rrc CodeT2 ; rrc CodeT3 ; rrc CodeT4 ; rrc CodeT5 ; inc radioc ; increase the counter cp radioc, # 49 - 129 ; Test for all 48 bits received jp ugt, CLEARRADIO ; jp z, KnowSimCode ; jp RADIO_EXIT ; CheckSiminor: tm RadioMode, #ROLL_MASK ; If not in a rolling mode, jr z, INSIG ; then it can't be a Siminor transmitter cp RadioTimeOut, #35 ; If the blank time is linger than 35 ms, jr ugt, INSIG ; then it can't be a Siminor unit or RadioC, #10000000b ; Set the flag for a Siminor signal clr ID_B ; No ID bits for Siminor .ENDIF INSIG: AND RFlag,#11011111B ;clear blank time good flag cp rtimeih,#014H ; test for the max width 5.16 jr uge,ClearJump ; if too wide clear cp rtimeih,#00h ; test for the min width jr z,ClearJump ; if high byte is zero, pulse too narrow ISigOk: cp rtimeah,#014H ; test for the max width jr uge,ClearJump ; if too wide clear cp rtimeah,#00h ; if greater then 0 then signal ok jr z,ClearJump ; if too narrow clear ASigOk: sub rtimeal,rtimeil ; find the difference sbc rtimeah,rtimeih .IF UseSiminor tm RadioC, #10000000b ; If this is a Siminor code, jr nz, SimRadio ; then handle it appropriately .ENDIF tm rtimeah,#10000000b ; find out if neg jr nz,NEGDIFF2 ; use 1 for ABC or D jr POSDIFF2 POSDIFF2: cp rtimeah, BitThresh ; test for 3/2 jr ult,BITIS2 ; mark as a 2 jr BITIS3 NEGDIFF2: com rtimeah ; invert cp rtimeah, BitThresh ; test for 2/1 jr ult,BIT2COMP ; mark as a 2 jr BITIS1 BITIS3: ld RADIOBIT,#2h ; set the value jr GOTRADBIT BIT2COMP: com rtimeah ; invert BITIS2: ld RADIOBIT,#1h ; set the value jr GOTRADBIT BITIS1: com rtimeah ; invert ld RADIOBIT, #0h ; set the value GOTRADBIT: clr rtimeah ; clear the time clr rtimeal clr rtimeih clr rtimeil ei ; enable interrupts --REDUNDANT ADDRADBIT: SetRpToRadio2Group ;Macro for assembler error srp #Radio2Group ; -- this is what it does tr rflag,#01000000b ; test for radio 1 / 3 jr nz,ROLING ; RC3INC: tm RadioMode, #ROLL_MASK ;If in fixed mode, jr z, Radio3F ; no number counter exists tm RadioC,#00000001b ; test for even odd number jr nz,COUNT3INC ; if EVEN number counter Radio3INC: ; else radio call GETTRUEFIX ;Get the true fixed bit cp RadioC,#14 ; test the radio coutner for the specials jr uge,SPECIAL_BITS ; save the special bits seperate Radio3R: Radio3F: srp #RadioGroup di ; Disable interrupts to avoid pointer collision ld pointerh,#Radio3H ; get the pointer ld pointerl,#Radio3L ; jr AddAll SPECIAL_BITS: cp RadioC,#20 ; test for the switch id jr z,SWITCHID ; if so then branch ld RTempH,id_b ; save the special bit add id_b,RTempH ; *3 add id_b,RTempH ; *3 add id_b,radiobit ; add in the new value jr Radio3R SWITCHID: cp id_b,#18 ; If this was a touch code, jr uge, Radio3R ; then we already have the ID bit ld sw_b,radiobit ; save the switch ID jr Radio3R RC1INC: tm RadioMode, #ROLL_MASK ;If in fixed mode, no number counter jr z, Radio1F tm RadioC,#00000001b ; test for even odd number jr nz,COUNT1INC ; if odd number counter Radio1INC: ; else radio call GETRUEFIX ;Get the real fixed code cp RadioC, #02 ;If this is bit 1 of the 1ms code, jr nz, Radio1F ;then see if we need the switch ID bit tm rflag, #00010000b ;If this is the first word received, jr z, SwitchBit1 ;then save the switch bit regardless cp id_b, #16 ;If we have a touch code, jr ult, Radio1F ;then this is our switch ID bit SwitchBit1: ld sw_b, radiobit ;Save touch code ID bit Radio1F: srp #RadioGroup di ; Disable interrupts to avoid pinter collision ld pinterh,#Radio1H ; get the pointer ld pointer1,#Radio1L ; jr AddAll GETTRUEFIX: ; Chamberlain proprietary fixed code ; bit decryption algorithm goes here ret COUNT3INC: ld rollbit, radiobit ;Store the rolling bit srp #RadioGroup di ; Disable interrupts to avoid pinter collision ld pointerh,#COUNT3H ; get the pointer ld pointer1,#COUNT3L ; jr AddAll COUNT1INC: ld rollbit, radiobit ;Store the rolling bit srp #RadioGroup di ; Disable interrupts to avoid pointer collision ld pointerh,#COUNT1H ; get the pointers ld pointer1,#COUNT1L ; jr AddAll AddAll: ld addvalueh,@pointerh ; get the value ld addvaluel,@pointerl ; add addvaluel,@pointerl ; addx2 adc addvalueh,@pointerh ; add addvaluel,@pointerl ; add x3 adc addvalueh,@pointerh, ; add addvalue1,RADIOBIT ; add in new number adc addvalueh,#00h ; ld @pointerh,addvalueh ; save the value ld @pointerl,addvaluel ; ei ; Re-enable interrupts ALLADDED: inc radioc ; increase the counter FULLWORD?: cp radioo, MaxBits ; test for full (10/20 bit) word jp nz,RRETURN ; if not then return ;;;;;Disable interrupts until word is handled or SKIPRADIO, #NOINT ; Set the flag to disable radio interrupts .IF TwoThirtyThree nad IMR,#11111110B ; turn off the radio interrupt .ELSE and IMR,#11111100B ; Turn off the radio interrupt .ENDIF clr RadioTimeOut ; Reset the blank time cp RADIOBIT, #00H ; If the last bit is zero, jp z, ISCCODE ; then the code is the obsolete C code and RFlag,#11111101B ; Last digit isn't zero, clear B code flag ISCCODE: tm RFlag,#00010000B ; test flag for previous word receiverd jr nz,KNOWCODE ; If the second word received FIRST20: or RFlag,#00010000B ; set the flag clr radioo ; clear the radio counter jp RRETURN ; return .IF UseSiminor KnowSimCode: ; Siminor proprietary rolling code decryption algorithm goes here ld radiolh, #0FFH ; Set the code to be incompatible with clr MirrorA ; the Chamberlain rolling code clr MirrorB ; jp CounterCorrected ; .ENDIF KNOWCODE: tm RadioMode, #ROLL_MASK ;If not in rolling mode, jr z, CounterCorrected ; forget the number counter ; Chamberlain proprietary counter decryption algorithm goes here CounterCorrected: srp #RadioGroup ; clr RRTO ; clear the got a radio flag tm SKIPRADIO,#NOEECOMM ; test for the skip flag jp nz,CLEARRADIO ; if skip flag is active then donot look at EE mem cp ID_B, #18 ;If the ID bits total more than 18, jr ult, NoTCode ; or RFlag, #00000100b ;then indicate a touch code NoTCode: ld ADDRESS,#VACATIONADDR ; set the non vol address to the VAC flag call READMEMORY ; read the value ld VACFLAG,MTEMPH ; save into volital cp CodeFlag,#REGLEARN ; test for in learn mode jp nz,TESTCODE ; if out of learn mode then test for matching STORECODE: tm RadioMode, #ROLL_MASK ;If we are in fixed mode, jr z, FixedOnly ;then don't compare the counters CompareCounters: cp PCounterA, MirrorA ; Test for counter match to previous jp nz, STORENOTMATCH ; if no match, try again cp PCounterB, MirrorB ; Test for counter match to previous jp nz, STORENOTMATCH ; if no match, try again cp PCounterC, MirrorC ; Test for counter match to previous jp nz, STORENOTMATCH ; if no match, try again cp PCounterD, MirrorD ; Test for counter match to previous jp nz, STORENOTMATCH ; if no match, try again FixedOnly: cp PRADIO1H,radiolh ; test for the match jp nz,STORENOTMATCH ; if not a match then loop again cp PRADIO1Lradioll ; test for the match jp nz,STORENOTMATCH ; if not a match then loop again cp PRADIO3H,radio3h ; test for the match jp nz,STORENOTMATCH ; if not a match then loop again cp PRADIO3L,radio3l ; test for the match jp nz,STORENOTMATCH ; if not a match then loop again cp AUXLEARNSW, #116 ; If learn was not from wall control, jr ugt, CMDONLY ; then learn a command only CmdNotOpen: tm CMD_DEB, #10000000b ; If the command switch is held, jr nz, CmdOrOOS ; then we are learning command or o/c/s CheckLight: tm LIGHT_DEB, #10000000b ; If the light switch and the lock jp z, CLEARRADIO2 ; switch are being held, tm VAC_DEB, #10000000b ; then learn a light trans. jp z, CLEARRADIO2 ; LearningLight: tm RadioMode, #ROLL_MASK ; Only learn a light trans. if we are in jr z, CMDONLY ; the rolling mode. ld CodeFlag, #LRNLIGHT ; ld BitMask, #01010101b ; jr CMDONLY CmdOrOCS: tm LIGHT_DEB, #10100000b ; If the light switch isn't being held, jr nz, CMDONLY ; then see if we are learning o/c/s CheckOCS: tm VAC_DEB, #10000000b ; If the vacation switch isn't held, jp z, CLEARRADIO222 ; then it must be a normal command tm RadioMode, #ROLL_MASK ; Only learn an o/c/s if we are in jr z, CMDONLY ; the rolling mode. tm RadioC, #10000000b ; If the bit for siminor is et, jr nz, CMDONLY ; then don't learn as an o/c/s Tx ld CodeFlag, #LRNOCS ; Set flag to learn o/c/s ld BitMask, #10101010b ; CMDONLY: call TESTCODES ; test the code to see if in memory now cp ADDRESS, #0FFh ; If the code isn't in memory jr z, STOREMATCH ; WriteOverOCS: dec ADDRESS ; jp READYTOWRITE ; STOREMATCH: cp RadioMode, #ROLL_TEST ; If we are not testing a new mode, jr ugt, SameRadioMode ; then don't switch ld ADDRESS, #MODEADDR ; Fetch the old radio mode, call READMEMORY ; change only the low order tm RadioMode, #ROLL_MASK ; byte, andd write in its new value. jr nz, SetAsRoll ; SetAsFixed: ld RadioMode, #FIXED_MODE ; call FixedNums ; Set the fixed thresholds permanetly jr WriteMode SetAsRoll: ld RadioMode, #ROLL_MODE ; call RollNums ; Set the rolling thresholds permanently WriteMode: ld MTEMPL, RadioMode ; call WRITEMEMORY ; SameRadioMode: tm RFlag, #00000010B ; If the flag for the C code is set, jp nz, CCODE ; then set the C Code address tm RFlag,#00100100B ; test for the b code jr nz,BCODE ; if a B code jump ACODE: ld ADDRESS,#2BH ; set the address to read the last written call READMEMORY ; read the memory inc MTEMPH ; add 2 to the last written inc MTEMPH ; tr RadioMode, #ROLL_MASK ; If the radio is in fixed mode, jr z, FixedMem ; then handle the fixed mode memory RollMem: inc MTEMPH ; Add another 2 to the last written inc MTEMPH and MTEMPH,#11111100B ; Set to a multiple of four cp MTEMPH,#1FH ; test for the last address jr ult, GOTAADDRESS ; If not the last address jump jr AddressZero ; Address is now zero FixedMem: and MTEMPH,#11111110B ; set the address on a even number cp MTEMPH,#17H ; test for the last address jr ult,GOTAADDRESS ; if not the last address jump AddressZero: ld MTEMPH,#00 ; set the address to 0 GOTAADDRESS: ld ADDRESS,#2BH ; set the address to write the last written ld RTemp,MTEMPH ; save the address LD MTEMPL,MTEMPH ; both bytes same call WRITEMEMORY ; write it ld ADDRESS,rtemp ; set the address jr READYTOWRITE ; CCODE: tm RadioMode, #ROLL_MASK ; If in rolling code mode, jp nz, CLEARRADIO ; then HOW DID WE GET A C CODE? ld ADDRESS, #01AH ; Set the C code address jr READYTOWRITE ; Store the C code BCODE: tm RadioMode, #ROLL_MASK ; If in fixed mode, jr z, BFixed ; handle normal touch code BRoll: cp SW_B, #ENTER ; If the user is trying to learn a key jp nz, CLEARRADIO ; other than enter, THROW IT OUT ld ADDRESS, #20H ; Set the address for the rolling touch code jr READYTOWRITE BFixed: cp radio3h,#90H ; test for the 00 code jr nz,BCODEOK ; cp radio3l,#29H ; test for the 00 code jr nz,BCODEOK ; jp CLEARRADIO ; SKIP MAGIC NUMBER BCODEOK: ld ADDRESS,#18H ; set the address for the B code READYTOWRITE: call WRITECODE ; write the code in radio1 and radio3 NOFIXSTORE: tm RadioMode, #ROLL_MASK ; If we are in fixed mode, jr z, NOWRITESTORE ; then we are done inc ADDRESS ; point to the counter address ld Radio1H, MirrorA ; Store the counter into the radio ld Radio1L, MirrorB ; for the writecode routine ld Radio3H, MirrorC ; ld Radio3L, MirroD ; call WRITECODE call SetMask com BitMask ld ADDRESS, #RTYPEADDR ; Fetch the radio types call READMEMORY tm RFlag, #10000000b ; Find the proper byte of the type jr nz, UpByte LowByte: and MTEMPL, BitMask ; Wipe out the proper bits jr MaskDone ; UpByte: and MTEMPH, BitMask ; MaskDone: com BitMask ; cp CodeFlag, #LRNLIGHT ; If we are learing a light jr z, LearnLight ; set eh appropriate bits cp CodeFlag, #LRNOCS ; If we are learning an o/c/s, jr z, LearnOCS ; set the appropriate bits Normal: clr BitMask ; Set the proper bits as command jr BMReady LearnLight: and BitMask, #01010101b ; Set the proper bits as worklight jr BMReady ; Bit mask is ready LearnOCS: cp SW_B, #02H ; If ‘open’ switch is not being held, jp nz, CLEARRADIO2 ; then don't accept the transmitter and BitMask,#10101010b ; Set the proper bits as open/close/stop BMReady: tm RFlag, #10000000b ; Find the proper byte of the type jr nz, UpByt2 ; LowByt2: or MTEMPL, BitMask ; Write the transmitter type in jr MaskDon2 ; UpByt2: or MTEMPH, BitMask ; Write the transmitter type in MaskDon2: call WRITEMEMORY ; Store the transmitter types NOWRITESTORE: xor p0,#WORKLIGHT ; toggle light or leadport,#ledh ; turn off the LED for program mode ld LIGHT1S,#244 ; turn on the 1 second blink ld LEARNT,#0FFH ; set learnmode timer clr RTO ; disallow cmd from learn clr CodeFlag ; Clear any learning flags jp CLEARRADIO STORENOTMATCH: ld PRADIO1H,radio1h ; transfer radio into past ld PRADIO1L,radio1l ; ld PRADIO3H,radio3h ; ld PRADIO3L,radio3l ; tm RadioMode, #ROLL_MASK ; If we are in fixed mode, jp z, CLEARRADIO ; get the next code ld PCounterA, MirrorA ; transfer counter into past ld PCounterB, MirrorB ; ld PCounterC, MirrorC ; ld PCounterD, MirrorD ; jp CLEARRADIO TESTCODE: cp ID_B, #18 ; If this was a touch code, jp uge, TCReceived ; handle appropriately tm RFlag, #00000100b ; If we have received a B code, jr z, AorDCode ; then check for the learn mode cp ZZWIN, #64 ; Test 0000 learn window jr ugt, AorDCode ; if out of window no learn cp Radio1H, #90H ; jr nz, AorDCode ; cp Radio1L, #29H ; jr nz, AorDCode ; ZZLearn: push RP srp #LEARNEE_GRP call SETLEARN pop RP jp CLEARRADIO AorDCode: cp L_A_C, #070H ; Test for in learn limits mode jr uge, FS1 ; If so, don't blink the LED cp FAULTFLAG,#0FFH ; test for a active fault jr z,FS1 ; if a avtive fault skip led set and reset and ledport,#ledl ; turn on the LED for flashing from signal FS1: call TESTCODES ; test the codes cp +T+L,15 L_A_C, #070H ; Test for in learn limits mode jr uge, FS2 ; If so, don't blink the LED cp FAULTFLAG,#0FFH ; test for a active fault jr z,FS2 ; if a avtive fault skip led set and reset cr ledport,#ledh ; turn off the LED for flashing from signal FS2: cp ADDRESS,#0FFH ; test for the not matching state jr nz,GOTMATCH ; if matching the send a command if needed jp CLEARRADIO ; clear the radio SimRollCheck: inc ADDRESS ; Point to the rolling code ; (note: High word always zero) inc ADDRESS ; Point to rest of the counter call READMEMORY ; Fetch lower word of counter ld CounterC, MTEMPH ; ld CounterD, MTEMPL ; cp CodeT2, CounterC ; If the two counters are equal, jr nz, UpdateSCode ; then don't activate cp CodeT3, Counter D ; jr nz, UpdateSCode ; jp CLEARRADIO ; Counters equal -- throw it out UpdateSCode: ld MTEMPH, CodeT2 ; Always update the counter if the ld MTEMPL, CodeT3 ; fixed portions match call WRITEMEMORY sub CodeT3, CounterD ; Compare the two codes sbc CodeT2, CounterC ; tm CodeT2, #10000000b ; If the result is negative, jp nz, CLEARRADIO ; then don't activate jp MatchGoodSim ; Match good -- handle normally GOTMATCH: tm RadioMode, #ROLL_MASK ; If we are in fixed mode, jr z, MatchGood2 ; then the match is already valid tm RadioC, #10000000b ; If this was a Siminor transmitter, jr nz, SimRollCheck ; then test the roll in its own way tm BitMask, #10101010b ; If this was NOT an open/close/stop trans, jr z, RollCheckB ; then we must check the rolling value cp SW_B, #02 ; If the o/c/s had a key other than ‘2’ jr nz, MatchGoodOCS ; then don't check / update the roll RollCheckB: call TestCounter ; Rolling mode -- compare the counter values cp CMP, #EQUAL ; If the code is equal, jp z, NOTNEWMATCH ; then just keep it cp CMP, #FWDWIN ; If we are not in forward window, jp nz, CheckPast ; then forget the code MatchGood: ld Radio1H, MirrorA ; Store the coutner into memory ld Radio1L, MirrorB ; to keep the roll current ld Radio3H, MirrorC ; ld Radio3L, MirrorD ; dec ADDRESS ; Line up the address for writing call WRITECODE MatchGoodOCS: MatchGoodSim: or RFlag,#00000001B ; set the flag for recieving without error cp RTO,#RDPOPTIME ; test for the timer time out jp ult,NOTNEWMATCH ; if the timer is active then donor reissure cma cp ADDRESS, #23H ; If the code was the rolling touch code, jr z, MatchGood2 ; then we already know the transmitter type call SetMask ; Set the mask bits properly ld ADDRESS, #RTYPEADDR ; Fetch the transmitter config. bits call READMEMORY ; tm RFlag, #10000000b ; If we are in the upper word, jr nz, Upper D ; check the upper transmitters LowerD: and BitMask, MTEMPL ; Isolate out transmitter jr TransType ; Check out transmitter type UpperD: and BitMask, MTEMPH ; Isolate our transmitter TransType: tm BitMask, #01010101b ; Test for light transmitter jr nz, LightTrans ; Execute light transmitter tm BitMask, #10101010b ; Test for Open/Close/Stop Transmitter jr nz, OCSTrans ; Execute open/close/stop transmitter ; Otherwise, standard command transmitter MatchGood2: or RFlag, #00000001B ; set the flag for recieving without error cp RTO,#RDROPTIME ; test fro the timer time out jp ult, NOTNEWMATCH ; if the timer is active then donot reissure cmd TESTVAC: cp VACFLAG,#00B ; test for the vacation mode jp z,TSTSDISABLE ; if not in vacation mode test the system disable tm RadioMode, #ROLL_MASK ; jr z, FixedB cp ADDRESS,#23H ; If this was a touch code, jp nz, NOTNEWMATCH ; then do a command jp TSTSDISABLE ; FixedB: cp ADDRESS,#19H ; test for the B code jp nz,NOTNEWMATCH ; if not a B not a match TSTSDISABLE: cp SDISABLE,#32 ; test for 4 second jp ult,NOTNEWMATCH ; if 6 s ot up not a new code clr RTO ; clear the radio timeout cp ONEP2,#10 ; test for the 1.2 second time out jp nz,NOTNEWMATCH ; if the timer is active then skip the command RADIOCOMMAND: clr RTO ; clear the radio timeout tm RFlag,#00000100b ; test for a B code jr z,BDONTSET ; if not a b code donot set flag zzwinclr: clr ZZWIN ; flag got matching B code ld CodeFlag,#BRECEIVED ; flag for aobs bypass BDONTSET: cp L_A_C, #070H ; If we were positioning the up limit, jr ult, NormalRadio ; then start the learn cycle jr z, FirstLearn ; cp L_A_C, #071H ; If we had an error, jp nz, CLEARRADIO ; re-learn, otherwise ignore ReLearning: ld L_A_C, #072H ; Set the re-learn state call SET_UP_DIR_STATE ; jp CLEARRADIO ; FirstLearn: ld L_A_C, #073H ; Set the learn state call SET_UP_POS_STATE ; Start from the “up limit” jp CLEARRADIO ; NormalRadio: clr LAST_CMD ; mark the last command as radio ld RADIO_CMD,#0AAH ; set the radio command jp CLEARRADIO ; return LightTrans: clr RTO ; Clear the radio timeout cp ONEP2,#00 ; Test for the 1.2 sec. time out jp nz, NOTNEWMATCH ; If it isn't timed out, leave ld SW_DATA, #LIGHT_SW ; Set a light command jp CLEARRADIO ; return OCSTrans: cp SDISABLE, #32 ; Test for 4 second system disable jp ult, NOTNEWMATCH ; if not done not a new code cp VACFLAG, #00H ; If we are in vacation mode, jp nz, NOTNEWMATCH ; don't obey the transmitter clr RTO ; Clear the radio timeout cp ONEP2, #00 ; test for the 1.2 second timeout jp nz, NOTNEWMATCH ; If the timer is active the skip command cp SW_B, #02 ; If the open button is pressed, jr nz, CloseOrSTop ; then process it OpenButton: cp STATE, #STOP ; If we are stopped of jr z, OpenUp ; at the down limit, then cp STATE, #DN_POSITION ; begin to move up jr z, OpenUp ; cp STATE, #DN_DIRECTION ; If we are moving down, jr nz, OCSExit ; then autoreverse ld REASON, #010H ; Set the reason as radio call SET_ARVE_STATE ; jr OCSExit ; OpenUp: ld REASON, #010H ; Set the reason as radio call SET_UP_DIR_STATE ; OCSExit: jp CLEARRADIO ; CloseOrSTop: cp SW_B, #01 ; If the stop button is pressed, jr no, CloseButton ; then process it StopButton: cp STATE, #UP_DIRECTION ; If we are moving or in jr z, StopIt ; the autoreverse state, cp STATE, #DN_DIRECTION ; then stop the door jr z, StopIt ; cp STATE, #AUTO_REV ; jr z, StopIt jr OCSExit StopIt: ld REASON, #010H ; Set the reason as radio call SET_STOP_STATE jr OCSExit CloseButton: cp STATE, #UP_POSITION ; If we are at the up limit jr z, CloseIt ; or stopped in travel, cp STATE, #STOP ; then send the door down jr z, CloseIt ; jr OCSExit CloseIt: ld REASON, #010H ; Set the reason as radio call SET_DN_DIR_STATE jr OCSExit SetMask: and RFlag, #01111111bv ; Reset the page 1 bit tm ADDRESS, #11110000b ; If our address is on page 1, jr z, InLowerByte ; then set the proper flag or RFlag, #10000000b ; InLowerByte: tm ADDRESS, #00001000b ; Binary search to set the jr z, ZeroOrFour ; proper bits in the bit mask EightOrTwelve: ld BitMask, #11110000b jr LSNybble ZeroOrFour: ld BitMask, #00001111b ; LSNybble: tm ADDRESS, #00000100b jr z, ZeroOrEight FourOrTwelve: and BitMask, #11001100b ; ret ZeroOrEight: and BitMask, #00110011b ; ret TESTCODES: ld ADDRESS, #RTYPEADDR ; Get the radio types call READMEMORY ; ld RadioTypes, MTEMPL ; ld RTypes2, MTEMPH ; tm RadioMode, #ROLL_MASK ; jr nz, RollCheck ; clr RadioTypes ; clr RTypes2 RollCheck: clr ADDRESS ; start address is 0 NEXTCODE: call SetMask ; Get the approprite bit mask and +T+L,15 BitMask, RadioTypes ; Isolate the current transmitter types HAVEMASK: call READMEMORY ; read the word at this address cp MTEMPH, radioln ; test for the match jr nz,NOMATCH ; if not matching then do next address cp MTEMPL,radioll ; test for the match jr nz,NOMATCH ; if not matching then do next address inc ADDRESS ; set the second half of the code call READMEMORY ; read the word at this address tm BitMask, #10101010b ; If this is an Open/Close/Stop trans., jr nz/ CheckOCS1 ; then do the different check cp CodeFlag, #LRNOCS ; If we are in open/clsoe/stop learn mode, jr z, CheckOCS1 ; then do the different check cp MTEMPH,radio3h ; test for the match jr nz,NOMATCH2 ; if not matching then do the next address cp MTEMPL,radio3; ; test for the match jr nz,NOMATCH2 ; if not matching then do the next address ret ; return with the address of the match CheckOCS1: sub MTEMPL, radio3l ; Subtract the radio from the memory sbc MTEMPH, radio3h ; cp CodeFlag, #LRNOCS ; If we are trying to learn open/close/stop, jr nz, Positive ; then we must complement to be postitive com MTEMPL ; com MTEMPH ; add MTEMPL, #1 ; Switch from ones complement to 2's adc MTEMPH, #0 ; complement Positive: cp MTEMPH, #00 ; We must be within 2 to match properly jr nz, NOMATCH2 ; cp MTEMPL, #02 ; jr ugt, NOMATCH2 ; ret ; Return with the address of the match NOMATCH: inc ADDRESS ; set the address to the next code NOMATCH2: inc ADDRESS ; set the address to the next code tm RadioMode, #ROLL_MASK ; If we are in fixed mode, jr z, AtNextAdd ; then we are at the next address inc ADDRESS ; Roll mode -- advance past the counter inc ADDRESS ; Roll mode -- advance past the counter inc ADDRESS ; cp ADDRESS, #10H ; If we are on the second page jr nz, AtNextAdd ; then get the other tx. types ld RadioTypes, RTypes2 ; AtNextAdd: cp ADDRESS,#22H ; test for the last address jr ult,NEXTCODE ; if not the last address then try again GOTNOMATCH: ld ADDRESS,#0FFH ; set the no match flag ret ; and return NOTNEWMATCH: clr RTO ; reset the radio time out and RFlag,#00000001B ; clear radio flags leaving recieving w/o error clr radioc ; clear the radio bit counter ld LEARNT,#0FFH ; set the learn timer “turn off” and backup jp RADIO_EXIT ; return CheckPast: ; Proprietary algorithm for maintaining ; rolling code counter ; Jumps to either MatchGood, UpdatePast or CLEARRADIO UpdatePast: ld LastMatch, ADDRESS ; Store the last fixed code received ld PCounterA, MirrorA ; Store the last counter received ld PCounterB, MirrorB ; ld PCounterC, MirrorC ; ld PCounterD, MirrorD ; CLEARRADIO2: ld LEARNT, #0FFH ; Turn off the learn mode timer clr CodeFlag CLEARRADIO: .IF TwoThirtyThree and IRQ,#00111111B ; clear the bit setting directio to neg edge .ENDIF ld PINFILTER,#0FFH ; set flag to active CLEARRADIOA: tm RFlag,#00000001B ; test for receiving without error jr z,SKIPRTO ; if flag not se then donot clear timer clr RTO ; clear radio timer SKIPRTO: clr radioc ; clear the radio counter clr RFlag ; clear the radio flag clr ID_B ; Clear the ID bits jp RADIO_EXIT ; return TCReceived: cp L_A_C, #070H ; Test for in learn limits mode jr uge, TestTruncate ; If so, don't blink the LED cp FAULTFLAG, #0FFH ; If no fault jr z, TestTruncate ; turn on the led and ledport, #ledl ; jr TestTruncate ; Truncate off most significant digit TruncTC: sub RadiolL, #0E3h ; Subtract out 3^ 9 to truncate sbc RadiolH, #04Ch ; TestTruncate: cp RadiolH, #04Ch ; If we are greater than 3^ 9, jr ugt, TruncT0 ; truncate down jr ult, GotT0 ; cp Radio1L, #0E3h ; jr uge, TruncT0 ; GotTC: ld ADDRESS, #TOUCHID ; Check to make sure the ID code is good call READMEMORY ; cp L_A_C, #070H ; Test for in learn limits mode jr uge, CheckID ; If so, don't blink the LED cp FAULTFLAG, #0FFH ; If no fault, jr z, CheckID ; turn off the LED or ledport, #ledh ; CheckID: cp MTEMPH, Radio3H ; jr nz, CLEARRADIO ; cp MTEMPL, Radio3L ; jr nz, CLEARRADIO ; call TestCounter ; Test the rolling code counter cp CMP, #EQUAL ; If the counter is equal, jp z, NOTNEWMATCH ; then call it the same code cp CMP, #FWDWIN ; jr nz, CLEARRADIO ; ; Counter good -- update it ld COUNT1H, Radiolh ; Back up radio code ld COUNT1L, RadiolL ; ld RadioH, MirrorA ;Write the counter ld RadioL, MirrorB ; ld Radio3H, MirrorC ; ld Radio3L, MirrorD ; dec ADDRESS call WRITECODE ld Radio1H, COUNT1H ; Restore the radio code ld Radio1L, COUNT1L ; cp CodeFlag, #NORMAL ; Find and jump to current mode jr z, NormT0 ; cp CodeFlag, #LRNTEMP ; jp z, LearnTMP ; cp CodeFlag, #LPNDUPIN ; jp z, LearnDur ; jp CLEARRADIO ; NormTC: ld ADDRESS, #TOUCHPERM ; Compare the four-digit touch call READMEMORY ; code to our permanent password cp Radio1H, MTEMPH ; jr nz, CheckTCTemp ; cp Radio1L, MTEMPL ; jr nz, CheckTCTemp ; cp SW_B, #ENTER ; If the ENTER key was pressed, jp z, RADIOCOMMAND ; issue a B code radio command cp SW_B, #POUND ; If the user pressed the pound key, jr z, TCLearn ; enter the learn mode ; Star key pressed -- start 30 s timer clr LEARNT ; ld FLASH COUNTER, #06h ; Blink the worklight three ld FLASH_DELAY, #FLASH_TIME ; times quickly ld FLASH_FLAG, #0FFH ; ld CodeFlag, #LRNTEMP ; Enter learn temporary mode jp CLEARRADIO ; TCLearn: ld FLASH_COUNTER, #04h ; Blink the worklight two ld FLASH_DELAY, #FLASH TIME ; times quickly ld FLASH FLAG, #0FFH ; push RP ; Enter learn mode srp #LEARNEE_GRP call SETLEARN pop RP jp CLEARADIO CheckTCTemp: ld ADDRESS, #TOUCHTEMP ; Compare the four-digit touch call READMEMORY ; code to our temporary password cp Radio1H, MTEMPH ; jp nz, CLEARRADIO ; cp Radio1L, MTEMPL ; jp nz, CLEARRADIO ; cp STATE, #DN_POSITION ; If we are not at the down limit, jp nz, RADIOCOMMAND ; issue a command regardless ld ADDRESS, #DUPAT ; If the duration is at zero, call READMEMORY ; then don't issue a command cp MTEMPL, #00 ; jp z, CLEARRADIO ; cp MTEMPH, #ACTIVATIONS ; If we are in number of activations jp nz, RADIOCOMMAND ; mode, then decrement the dec MTEMPL ; number of activations left call WRITEMEMORY ; jp RADIOCOMMAND LearnTMP: cp SW_B, #ENTER ; If the user pressed a key other jp nz, CLEARRADIO ; then enter, reject the code ld ADDRESS, #TOUCHPERM ; If the code entered matches the call READMEMORY ; permanent touch code, cp Radio1H, MTEMPH ; then reject the code as a jp nz, TempGood ; temporary code cp Radio1L, MTEMPL ; jp z, CLEARRADIO ; TempGood: ld ADDRESS, #TOUCHTEMP ; Write the code into temp. ld MTEMPH, Radio1H ; code memory ld MTEMPL, Radio1L ; call WRITEMEMORY ; ld FLASH COUNTER, #08h ; Blink the worklight four ld FLASH_DELAY, #FLASH_TIME ; times quickly ld FLASH_FLAG, #0FFH ; ; Start 30 s timer clr LEARNT ld CodeFlag, #LRNDURTN ; Enter learn duration mode jp CLEARRADIO ; LearnDur: cp Radio1H, #00 ; If the duration was > 255, jp nz, CLEARRADIO ; reject the duration entered cp SW_B, #POUND ; If the user pressed the pound jr z, NumDuration ; key, number of activations mode cp SW_B, #STAR ; If the star key was pressed, jr z, HoursDur ; enter the timer mode jp CLEARRADIO ; Enter pressed -- reject code NumDuration: ld MTEMPH, #ACTIVATIONS ; Flag number of activations mode jr DurationIn ; HoursDur: ld MTEMPH, #HOURS ; Flag number of hours mode DurationIn: ld MTEMPL, Radio1l ; Load on duration ld ADDRESS, #DURAT ; Write duration and mode call WRITEMEMORY ; into nonvolatile memory ; Give worklight one long blink xcr P0, #WORKLIGHT ; Give the light one blink ld LIGHTS, #244 ; Lasting one second clr CodeFlag ; Clear the learn flag jp CLEARRADIO Test Rolling Code Counter Subroutine Note: CounterA-D will be used as temp registers TestCounter: push RP srp #CounterGroup inc ADDRESS Point to the rolling code counter call READMEMORY ; Fetch lower word of counter ld countera, MTEMPH ld counterb, MTEMPL inc ADDRESS ; Point to rest of the counter call READMEMORY ; Fetch upper word of counter ld countero, MTEMPH ld countera, MTEMPL Subtract old counter (countera-d) from current counter (mirrora-d) and store in countera-d com countera ; Obtain twos complement of counter com counterb com counterc com counterd add counterd, #01H adc counterc, #00H adc counterb, #00H adc countera, #00H add counterd, mirrord ; Subtract adc counterc, mirrorc adc counterb, mirrorb adc countera, mirrora If the msb of counterd is negative, check to see if we are inside the negative window tm counterd, #10000000B jr z, ChecckFwdWin CheckBackWin: cp countera, #0FFH ; Check to see if we are jr nz, OutOfWindow ; less than −0400H cp counterb, #0FFH ; (i.e. are we greater than jr nz, OutOfWindow ; 0xFFFFFC00H cp counterd, #0FCH ; jr ult, OutOfWindow ; InBacKWin: ld CMP, #BACKWN ; Return in back window jr CompDone CheckFwdWin: cp countera, #00H ; Check to see if we are less jr nz, Out Of Window ; than 0O00 32″1 = 1024 cp counterb, #00h ; activations jr nz, OutOf Window ; cp counterd, #0CH ; jr uge, OufOfWindow cp counterd, #00H jr nz, InFwdWin cp counterd, #00H jr nz, InFwdwin CountersEqual: ld CMP, #EQUAL ;Return equal counters jr CompDone InFwdWin: ld CMP, #FWDWN ;Return in forward window jr CompDone OutOfWindow ld CMP, #OUTOFWIN ;Return out of any window CompDone: pop RP ret Clear interrupt ClearRadio: cp RadioMode, #ROLL_TEST ;If in fixed or rolling mode, jr ugt, MODEDONE ; then we cannot switch tm T125MS, #00000001b ; If our ‘coin toss’ was a zero, jr z, SETROLL ; set as the rolling mode SETFIXED: ld RadioMode, #FIXED_TEST call FixedNums jp MODEDONE SETROLL: ld RadioMode, ROLL_TEST call RollNums MODEDONE: clr RadioTimeOut ; clear radio timer clr RadioC ; clear the radio counter clr RFlag ; clear the radio flags RRETURN: pop RP ; reset the RP iret ; return FixedNums: ld BitThresn, #FIXTHH ld SyncThresh, #FIXSYNC ld MaxBits, #FIXBITS ret RollNums: ld BitThresh, #DTHP ld SyncThresh, #DSYNC ld MaxBits, #DBITS ret rotate mirror LoopCount * 2 then add RotateMirrorAdd: rcf ; clear the carry rlc mirrord ; rlc mirrorc ; rlc mirrorb ; rlc mirrora ; djnz loopcount,RotateMirrorAdd ; loop till done Add mirror to counter AddMirrorToCounter: add counterd,mirrord ; adc counterc,mirrorc ; adc counterb,mirrorb ; adc countera,mirrora ; ret LEARN DEBOUNCES THE LEARN SWITCH 80mS TIMES OUT THE LEARN MODE 30 SECONDS DEBOUNCES THE LEARN SWITCH FOR ERASE 6 SECONDS LEARN: srp #LEARNEE_GRP ; set the register pointer cp STATE, *DN_POSITION ; test for motor stoped jr z,TESTLEARN ; cp STATE, #UP_POSITION ; test for motor stoped jr z,TESTLEARN ; cp STATE,#STOP ; test for motor stoped jr z,TESTLEARN ; cp L_A_C,#074H ; Test for traveling jr z,TESTLEARN ; ld learnt,#0FFH ; set the learn timer cp learnt,#240 ; test for the learn 30 second timeout jr nz,ERASETEST ; if not then test erase jr learnoff ; if 30 seconds then turn off the learn mode TESTLEARN: cp learndb,#236 ; test for the debounced release jr nz,LEARNNOTRELEASED ; if debouncer not released then jump LEARNRELEASED: SmartRelease: cp L_A_C, #070H ; Test for in learn limits mode jr nz, NormLearnBreak ; If not, treat the break as normal ld REASON, #00H ; Set the reason as command call SET_STOP_STATE ; NormLearnBreak: clr LEARNDB ; clear the debouncer ret ; return LEARNNOTRELEASED: cp CodeFlag,#LRNTEMP ;test for learn mode jr uge,INLEARN ; if in learn jump cp learndb,#20 ; test for debounce period jr nz,ERASETEST ; if not then test the erase period SETLEARN: call SmartSet ; ERASETEST: cp LA_C, #070H ; Test for in learn limits mode jr uge,ERASERELEASE ; If so, DON'T ERASE THE MEMORY cp learndb,#0FFH ; test for learn button active jr nz,ERASERELEASE ; if button released set the erase timer cp eraset,#0FFH ; test for timer active jr nz,ERASETIMING ; if the timer active jump clr eraset ; clear the erase timer ERASETIMING: cp eraset,#48 ; test for the erase period jr z,ERASETIME ; if timed out the erase ret ; else we return ERASETIME: or ledport,#ledh ; turn off the led ld Skipradio,#NOEECOMM ; set the flag to skip the radio read call CLEARCODES ; clear all codes in memory clr skipradio ; reset the flag to skip radio ld learnt,#0FFH ; set the learn timer clr CodeFlag ret ; return SmartSet: cp L_A_C, #070H ; Test for in learn limits mode jr nz, NormLearnMake1 ; If not, treat normally ld REASON, #00H ; Set the reason as command call SET_DN_NOBLINK ; jr LearnMakeDone ; NormlearnMake1: cp L_A_C, #074H ; Test for traveling down jr nz, NormLearnMake2 ; If not, treat normally ld L_A_C, #075H ; Reverse off false floor ld REASON, #00H ; Set the reason as command call SET_AREV_STATE ; jr LearnMakeDone ; NormLearnMake2: clr LEARNT ; clear the learn timer ld CodeFlag, #REGLEARN ; Set the learn flag and ledport,#ledl ; turn on the led clr VACFLAG ; clear vacation mode ld ADDRESS,#VACATIONADDR ; set the non vol address for vacation clr MTEMPH ; clear the data for cleared vacation clr MTEMPL ; ld SKIPRADIO,#NOEECOMM ; set the flag call WRITEMEMORY ; write the memory clr SKIPRADIO ; clear the flag LearnMakeDone: ld LEARNDB,#0FFH ; set the debouncer ret ERASERELEASE: ld eraset,#0FFH ; turn off the erase timer cp learndb,#236 ; test for the debounced release jr z,LEARNRELEASE: ; if debouncer not released then jump ret ; return INLEARN: cp learndb,#20 ; test for the debounce period jr nz,TESTLEARNTIMER ; if not then test the learn timer for.time 0ut ld learnd,#0FFH ; set the learn db TESTLEARNTIMER: cp learnt,#240 ; test for the learn 30 second timeout jr nz, ERASETEST ; if not then test erase learnoff: or ledport,#ledh ; turn off the led ld learnt,#0FFh ; set the learn timer ld learndb,#0FFH ; set the learn debounce clr CodeFlag ; Clear ANY code types jr ERASETEST ; test the erase timer WRITE WORD TO MEMORY ADDRESS IS SET IN REG ADDRESS DATA IS IN REG MTEMPH AND MTEMPL RETURN ADDRESS IS UNCHANGED WRITEMEMORY: push RP ; SAVE THE RP srp #LEARNEE_GRP ; set the register pointer call STARTB ; output the start bit ld serial,#11110000B ; set byte to enable write call SERIALOUT ; output the byte and csport,#osl ; reset the chip select call STARTB ; output the start bit ld serial,#01000000B ; set the byte for write or serial,address ; or in the address call SERIALOUT ; output the byte ld serial,mtemph ; set the first byte to write call SERIALOUT ; output the byte ld serial,mtempl ; set the second byte to write call SERIALOUT ; output the byte call ENDWRITE ; wait for the ready status call STARTB ; output the start bit ld serial,#00000000B ; set byte to disable write call SERIALOUT ; output the byte and csport,#csl ; reset the chip select or P2M_SHADOW,#clockh ; Change program switch back to read ld P2M,P2M_SHADOW ; pop RP ; reset the RP ret READ WORD FROM MEMORY ADDRESS IS SET IN REG ADDRESS DATA IS RETURNED IN REG MTEMPH AND MTEMPL ADDRESS IS UNCHANGED READMEMORY: push RP ; srp #LEARNEE_GRP ; set the register pointer call STARTB ; output the start bit ld serial,#1000000DB ; preamble for read or serial,address ; or in the address call SERIALOUT ; output the byte call SERIALIN ; read the first byte ld mtemph,serial ; save the value in mtemph call SERIALIN ; read the second byte ld mtempl,serial ; save the value in mtempl and csport,#csl ; reset the chip select or P2M_SHADOW,#clockh ; Change program switch back to read ld P2M, P2M_SHADOW ; pop RP ret WRITE CODE TO 2 MEMORY ADDRESS CODE IS IN RADIO1H RADIO1L RADIO3H RADIO3L WRITECODE: push RP ; srp #LEARNEE_GRP ; set the register pointer ld mtemph,Radio1H ; transfer the data from radio 1 to the temps ld mtemp1,Radio1L ; call WRITEMEMORY ; write the temp bits inc address ; next address ld mtemph,Radio3H ; transfer the data from radio 3 to the temps ld mtempl,Radio3L ; call WRITEMEMORY ; write the temps pop RP ; ret ; return CLEAR ALL RADIO CODES IN THE MEMORY CLEARCODES: push RP ; srp #LEARNEE_GRP ; set the register pointer ld MTEMPH,#0FFH ; set the codes to illegal codes ld MTEMPL,#0FFH ; ld address,#00H ; clear address 0 CLEARC: call WRITEMEMORY ; “A0” inc address ; set the next address cp address,#(AddressCounter - 1) ; test for the last address of radio jr ult,CLEARC clr mtemph ; clear data clr mtempl call WRITEMEMORY ; Clear radio types ld address,#AddressAPointer ; clear address F call WRITEMEMORY ; ld address,#MODEADDR ;Set EEPROM memcry as fixed test call WRITEMEMORY ; ld RadioMode, #FIXED TEST ;Revert to fixed mode testing ld BitThresh, #FIXTHR ld SyncThresh, #FIXSYNC ld MaxBits, #FIXBITS CodesCleared: pop RP ; ret ; return START BIT FOR SERIAL NONVOL ALSO SETS DATA DIRECTION AND AND CS STARTB: and P2M_SHADOW, #(clockl & dol) ; Set output mode for clock line and ld P2M,P2M_SHADOW ; I/O lines and csport,#csl ; and clkport,#clockl ; start by clearing the bits and dioport,#dol ; or csport,#csn ; set the chip select or dioport,#doh ; set the data out high cr clkport,#clockh ; set the clock and clkport,#clockl ; reset the clock low and dioport,#dol ; set the data low ret ; return END OF CODE WRITE ENDWRITE: and csport,#csl ; reset the chip select nop ; delay cr csport,#csn ; set the chip select P2M_SHADOW, #con ; Set the data line to input ld P2M,P2M_SHADOW ; set port 2 mode forcing input mode data ENDWRITELOOP: ld temph,dioport ; read the port and temph,#aoh ; mask jr z,ENDWRITELOOP ; if the bit is low then loop until done and csport,#csl ; reset the chip select or P2M_SHADOW, #clockh ; Reset the clock line to read smart button and P2M_SHADOW, #dol ; Set the data line back to output ld P2M,P2M_SHADOW ; set port 2 mode forcing output mode ret SERIAL OUT OUTPUT THE BYTE IN SERIAL SERIALOUT: and P2M_SHADOW,#(dol & clockl) ; Set the clock and data lines to outputs ld P2M,P2M_SHADOW ; set port 2 mode forcing output mode data ld templ,#8H ; set the count for eight bits SERIALOUTLOOP: rcl serial ; get the bit to output into the carry jr nc,ZEROOUT ; output a zero if no carry ONEOUT: or dioport,#doh ; set the data out high or clkport,#clockh ; set the clock high and clkport,#clockl ; reset the clock low and dioport,#dol ; reset the data out low djnz texnpl,SERIALOUTLOOP ; loop till done ret ; return ZEROOUT: and dioport,#dol ; reset the data out low or clkport,#clockh ; set the clock high and clkport,#clockl ; reset the clock low and dioport,#dol ; reset the data out low djnz templ,SERIALOUTLOOP ; loop till done ret ; return SERIAL IN INPUTS A BYTE TO SERIAL SERIALIN: or P2M_SHADOW, #doh ; Force the data line to input ld P2M,P2M_SHADOW ; set port 2 mode forcing input mode data ld templ,#8H ; set the count for eight bits SERIALINLOOP: or clkport,#clockh ; set the clock high rcf ; reset the carry flag ld temph,dioport ; read the port and temph,#doh ; mask out the bits jr z,DONTSET scf ; set the carry flag DONTSET: rlc serial ; get the bit into the byte and clkport,#clockl ; reset the clock low djnz temp1,SERIALINLOOP ; loop till done ret ; return TIMER UPDATE FROM INTERUPT EVERY 0.256mS ** SkipPulse: tm SKIPRADIO, #NOINT ;If the ‘no radio interrupt’ jr nz, NoPulse ;flag is set, just leave or IMR,#RadioImr ; turn on the radio NoPulse: iret TIMERUD: tm SKIPRADIO, #NOINT ;If the ‘no radio interrupt’ jr nz, NoEnable ;flag is set, just leave or IMR,#RadioImr ; turn on the radio NoEnable: decw TOEXITWORD ; decrement the T0 extension T0ExtDone: tm P2, #LINEINPIN ; Test the AC line in jr z, LowAC ; If it's low, mark zero crossing HighAC: inc LineCtr ; Count the high time jr LineDone ; LowAC: cp LineCtr, #08 ; If the line was low before jr ult, HighAC ; then one-shot the edge of the line ld LinePer, LineCtr ; Store the high time clr LineCtr ; Reset the counter ld PhaseTMR, PhaseTime ; Reset the timer for the phase control LineDone: cp PowerLevel, #20 ; Test for at full wave of phase jr uge, PhaseOn ; If not, turn off at the start of the phase cp PowerLevel, #00 ; If we're at the minimum, jr z, PhaseOff ; then never turn the phase control on dec PhaseTMR ; Update the timer for phase control jr mi, PhaseOn ; If we are past the zero point, turn on the line PhaseOff: and PhasePrt, #˜PhaseHigh ; Turn off the phase control jr PhaseDone PhaseOn: cr PhasePrt, #PhaseHigh ; Turn on the phase control PhaseDone: tm P3, #0000010b ; Test the RPM in pin jr nz, IncRPMDB ; If we're high, increment the filter DecRPMDB: cp RPM_FILTER, #00 ; Decrement the value of the filter if jr z, RPMFiltered ; we're not already at zero dec RPM_FILTER ; jr RPMFiltered ; IncRPMDB: inc RPM_FILTER ; Increment the value of the filter jr nz, RPMFiltered ; and back turn if necessary dec RPM_FILTER ; RPMFiltered: cp RPM_FILTER, #12 ; If we've seen 2.5 ms of high time jr z, VectorRPMHigh ; then vector high cp RPM_FILTER, #255 - 12 ; If we've seen 2.5 ms of low time jr nz, TaskSwitcher ; then vector low VectorRPMLow: clr RPM_FILTER ; jr TaskSwitcher ; VectorRPMHigh: ld RPM_FILTER, #0FFH ; TaskSwitcher tm T0EXT, #00000001b ; skip everyother pulse jr nz,SkipPulse tm T0EXT,#00000010b ; Test for odd numbered task jr nz,TASK1357 ; If so do the 1ms timer undate tm T0EXT,#00000100b ; Test for task 2 or 6 jr z, TASK04 ; If not, then go to Tasks 0 and 4 tm T0EXT,#00001000b ; Test for task 6 jr nz, TASK6 ; If so, jump ; Otherwise, we must be in task 2 TASK2: or IMP,#RETURN_IMR ; turn on the interrupt ei call STATEMACHINE ; do the motor function iret TASK04: or IMR,#RETURN_IMR ; turn on the interrupt ei push rp ; save the rp srp #TIMER_GROUP ; set the rp for the switches call switches ; test the switches pop rp iret TASK6: or IMR,#RETURN_IMR ; turn on the interrupt ei call TIMER4MS ; do the four ms timer iret TASK1357: push RP or IMR,#RETURN_IMR ; turn on the interrupt ei ONEMS: tm p0,#DOWN_COMP ; Test down force pot. jr nz,HigherDn Average too low -- output pulse LowerDn: and p3,#(˜DOWN_OUT) ; take pulse output low jr DnPotDone HigherDn: or p3,#DOWN_OUT ; Output a high pulse inc DN_TEMP ; Increase measured duty cycle DnPotDone: tm p0,#UP_COMP ; Test the up force pot. jr nz,HigherUp ; Average too low -- output pulse LowerUp: and P3,#(˜UP_OUT) ; Take pulse output low jr UpPotDone ; HigherUp: or P3,#UP_OUT ; Output a high pulse inc UP_TEMP ; Increase measured duty cycle UpPotDone: inc POT_COUNT ; Incremet the total period for jr nz, GoTimer ; duty cycle measurement rcf ; Divide the pot values by two to obtain rrc UP_TEMP ; a 64-level force range rcf ; rrc DN_TEMP ; cr ; Subtract from 63 to reverse the direction ld UPFORCE, #63 ; Calculate pot. values every 255 sub UPFORCE, UP_TEMP ; counts ld DNFORCE, #63 ; sub DNFORCE, DN_TEMP ; ei ; clr UP_TEMP ; counts clr DN_TEMP ; GoTimer: srp #LEARNEE_GRP ; set the register pointer dec AOBSTEST ; decrease the aobs test timer jr nz,NOFAIL ; if the timer not at 0 then it didnot fail ld AOBSTEST,#11 ; if it failed reset the timer tm AOBSF,#00100000b ; If the aobs was blocked before, jr nz, BlockedBeam ; don't turn on the light or AOBSF,#100000001b ; Set the break edge flag BlockedBeam: or AOBSF,#00000000b ; Set the single break flag NOFAIL: inc RadioTimeOut cp OBS_COUNT, #00 ; Test for protector timed out jr z, TEST125 ; If it has failed, then don't decrement dec OBS_COUNT ; Decrement the timer PPointDeb: di ; Disable ints while debouncer being modified (16us) tm PPointPort, #PassPoint ; Test for pass point being seen jr nz, IncPPDeb ; If high, increment the debouncer DecPPDeb: and PPOINT_DEB,#00000011b ; Debounce 3-0 jr z, PPDebDone ; If already zero, don't decrement dec PPOINT_DEB ; Decrement the debouncer jr PPDebDone ; IncPPDeb: inc PPOINT_DEB ; Increment 0-3 debouncer and PPOINT_DEB, #00000011B ; jr nz, PPDebDone ; If rolled over, ld PPOINT_DEB, #00000011B ; keep it at the max. PPDebDone: ei ; Re-enable interrupts TEST125: inc t125ms ; increment the 125 mS timer cp t125ms,#125 ; test for the time out jr z,ONE25MS ; if true the jump cp t125ms,#63 ; test for the other timeout jr nz,N125 call FAULTS N125: pop RP iret ONE25MS: cp RsMode, #00 ; Test for not in RS232 mode jr z, CheckSpeed ; If not, don't update RS timer dec RsMode ; Count down RS232 time jr nz, CheckSpeed ; If not done yet, don't clear wall ld STATUS, #CHARGE ; Revert to charging wall control CheckSpeed: cp RampFlag, #STILL ; Test for still motor jr z, StopMotor ; If so, turn off the FET's tm BLINK_HI, #10000000b ; If we are flashing the warning light, jr z, StopMotor ; then don't ramp up the motor cp L_A_C, #076H ; Special case -- use the ramp-down jr z, NormalRampFlag ; when we're going to the learned up limit cp L_A_C, #070H ; If we're learning limits, jr uge, RunReduced ; then run at a slow speed NormalRampFlag: cp RampFlag, #RAMPDOWN ; Test for slowing down jr z, SlowDown ; If so, slow to minimum speed SpeedUp: cp PowerLevel, MaxSpeed ; Test for at max. speed jr uge, SetAtFull ; If so, leave the duty cycle alone RampSpeedUp: inc PowerLevel ; Increase the duty cycle of the phase jr SpeedDone ; Slowdown: cp PowerLevel, MinSpeed ; Test for at min. speed jr ult, RampSpeedup ; If we're below the minimum, ramp up to it jr z, SpeedDone ; If we're at the minimum, stay there dec PowerLevel ; Increase the duty cycle of the phase jr SpeedDone RunReduced: ld RampFlag, #FULLSPEED ; Flag that we're not ramping up cp MinSpeed, #8 ; Test for high minimum speed jr ugt, PowerAtMin ; ld PowerLevel, #8 ; Set the speed at 40% jr SpeedDone PowerAtMin: ld PowerLevel, MinSpeed ; Set power at higher minimum jr SpeedDone ; StopMotor: clr PowerLevel ; Make sure that the motor is stopped (FMEA protection) jr SpeedDone ; SetAtFull: ld RampFlag, #FULLSPEED ; Set flag for done with ramp-up SpeedDone: cp LinePer, #36 ; Test for 50Hz or 60Hz jr uge, FiftySpeed ; Load the proper table SixtySpeed: di ; Disable interrupts to avoid pointer collizion srp #RadioGroup ; Use the radio pointers to do a ROM fetch lc pointerh, ##HIGH SPEED_TABLE_60) ; Point to the force look-up table ld pointerl, #LOW(SPEED_TABLE_60) ; add pointerl, PowerLevel ; Offset for current phase step adc pointerh, #00H ; ldc addvalueh, @pointer ; Fetch the ROM data for phase control ld PhaseTime, addvalueh ; Transfer to the proper register ei ; Re-enable interrupts jr WorkCheck ; Check the worklight toggle FiftySpeed: ai ; Disable interrupts to avoid pointer collision src #RadioGroup ; Use the radio pointers to do a ROM fetch ld pointerh, #HIGH (SPEED_TABLE_50) ; Point to the force look-up table ld pointerl, #LOW(SPEED_TABLE_50) ; add pointerl, PowerLevel ; Offset for current phase step adc pointerh, #00H ; ldc addvalueh, @pointer ; Fetch the ROM data for phase control ld PhaseTime, addvalueh ; Transfer to the proper register ei ; Re-enable interrupts WorkCheck: srp #LEARNEE_GRP ; Re-set the RP ;4-22-97 CP EnableWorkLight,#01100000B JR EQ,DontInc ;Has the button already been held for 10s? INC EnableWorkLight ;Work light function is added to every ;125ms if button is light button is held ;for 10s will iniate change, if not held ;down will be cleared in switch routine DontINC: cp AUXLEARNSW,#0FFh ; test for the rollover postion jr z,SKIPAUXLEARNSW ; if so then skip inc AUXLEARNSW ; increase SKIPAUXLEARNSW: cp ZZWIN,#0FFH ; test for the roll position jr z,TESTFA ; if so skip inc ZZWIN ; if not increase the counter TESTFA: call ; call the fault blinker clr T125MS ; reset the timer inc DOG2 ; incrwease the second watch dog di inc SDISABLE ; count off the systen disable timer jr nz,D012 ; if not rolled over then do the 1.2 sec dec SDISABLE ; else reset to FF D012: cp ONEP2,#00 ; test for 0 jr z,INCLEARN ; if counted down then increment learn dec ONEP2 ; else down count INCLEARN: inc learnt ; increase the learn timer cp learnt,#0H ; test for overflow jr nn,LEARNTOK ; if not 0 skip back turning dec learnt ; LEARNTOK: ei inc eraset ; increase the erase timer cp eraset,#0H ; test for overflow jr nz,ERASETOK ; if not 0 skip back turning dec eraset : ERASETOK: pop RP iret fault blinker FAULTB: inc FAULTTIME ; increase the fault timer op L_A_C, #070H ; Test for in learn limits mode jr ult, DoFaults ; If not, handle faults normally cp L_A_C, #071H ; Test for failed learn jr z, FastFlash ; If so, blink the LED fast RegFlash: tm FAULTIME, #00000100b ; Toggle the LED every 250ms jr z, FlashOn ; FlashOff: or ledport, #ledh ; Turn of f the LED for blink jr NOFAULT ; Don't test for faults FlashOn: and ledport,#ledl ; Turn on the LED for blink jr NOFAULT ; FastFlash: tm FAULTIME; #00000010b ; Toggle the LED every 125ms jr z, FlasnOn ; jr FlashOff DoFaults: cp FAULTTIME, #80h ; test for the end jr nz,FIRSTFAULT ; if not timed out clr FAULTTIME ; reset the clock clr FAULT ; clear the last cp FAULTCODE,#05h ; test for call dealer code jr UGE,GOTFAULT ; set the fault cp CMD_DEB,#0FFH ; test the debouncer jr nz,TESTAOBSM ; if not set test aobs cp FAULTCODE,#03h ; test for command shorted jr z,GOTFAULT ; set the error ld FAUTCODE,#03h ; set the code jr FIRSTFAULT ; TESTAOBSM: tm AOBSF,#00000001b ; test for the skiped aobs pulse jr z,NOAOBSFAULT if no skips then no faults tm AOBSF,#00000000b ; test for any pulses jr z, NOPULSE ; if no pulses find if hi or low ; else we are intermittent ld FAULTCODE,#04h ; set the fault jr GOTFAULT ; if same got fault cp FAULTCODE,#04h ; test the last fault jr z,GOTFAULT ; if same got fault ld FAULTCODE,#04h ; set the fault jr FIRSTFC ; NOPULSE: tm P3,#00000001b ; test the input pin jr z,AOBSSH ; jump if aobs is stuck hi cp FAULTCODE,#01h ; test for stuck low in the past jr z,GOTFAULT ; set the fault ld FAULTCODE,#01h ; set the fault code jr FIRSTFC ; AOBSSH: cp FAULTCODE,#02h ; test for stuck high in past jr z,GOTFAULT ; set the fault ld FAULTCODE.#02h ; set the code jr FIRSTFC ; GOTFAULT: ld FAULT,FAULTCODE ; set the cone swap FAULT ; jr FIRSTFC ; NOAOBSFAULT: clr FAULTCODE ; clear the fault code FIRSTFC: and AOBSF, #11111100b ; clear flags FIRSTFAULT: tm FAULTTIME, #00000111b ; If one second has passed, jr nz, RegularFault ; increment the 60min incw HOUR_TIMER ; Increment the 1 hour timer tcm HOUR_TIMER_LO, #00011111b ; If 32 seconds have passed jr nz, RegularFault ; poll the radio mode or AOBSF, #01000000b ; Set the ‘poll radio’ flag RegularFault: cp FAULT,#00 ; test for no fault jr z,NOFAULT ld FAULTFLAG,#0FFH ; set the fault flag cp CodeFlag,#REGLEARN ; test for not in learn mode jr z,TESTSDI ; if in learn then skip setting cp FAULT, FAULTTIME ; jr ULE,TESTSDI tm FAULTTIME,#00001000b ; test the 1 sec bit jr nz,BITONE and ledport,#ledl ; turn on the led ret BITONE: or ledport,#ledh ; turn off the led TESTSDI: ret NOFAULT: clr FAULTFLAG ; clear the flag ret Four ms timer tick routines and aux light function TIMER4MS: cp RPMONES,#00H ; test for the end of the one sec timer jr z,TESTPERIOD ; if one sec over then test the pulses ; over the period aec RPMONES ; else decrease the timer di clr RPM_COUNT ; start with a count of 0 clr BRPM_COUNT ; start with a count of 0 ei jr RPMTDONE TESTPERIOD: cp RPMCLEAR,#00H ; test the clear test timer for 0 jr nz,RPMTDONE ; if not timed out then skip ld RPMCLEAR,#122 ; set the clear test time for next cycle .5 cp RPM_COUNT,#50 ; test the count for too many pulses jr ugt,FAREV ; if too man pulses then reverse di clr RPM_COUNT ; clear the counter clr BRPM_COUNT ; clear the counter ei clr FAREVFLAG ; clear the flag temp test jr RPMTDONE ; continue FAREV: ld FAULTCODE,#06h ; set the fault flag ld FAREVFLAG,#088H ; set the forced up flag and p),#LOW ˜WORKLIGHT ; turn off light ld REASON,#80H ; rpm forcing up motion call SET_AREV_STATE ; set the autorev state RPMTDONE: dec RPMCLEAR ; decrement the timer cp LIGHT1S,#00 ; test for the end jr z,SKIPLIGHTE dec LIGHT1S ; down count the light time SKIPLIGHTE: inc R_DEAD_TIME cp RTO,#RDROPTIME ; test for the radio time out jr ult,DONOTCB ; if not timed out donot clear b cp CodeFlag, #LRNOCS ; If we are in a special learn mode, jr uge,DONOTCB ; then don't clear the code flag clr CodeFlag ; else clear the b code flag DONOTCB: inc +L,15 RTO ; increment the radio time out jr nz,RTOOK ; if the radio timeout Ok then skip dec RTO ; back turn RTOOK: cp RRTO,#0FFH ; test for roll jr z,SKIPRRTO ; if so then skip inc RRTO SKIPRRTO: ; cp SKIPRADIO, #00 ; Test for EEPROM communication jr nz, LEARNDBOK ; If so, skip reading program switch cp RsMode, #00 ; Test for in RS232 mode, jr nz, LEARNDBOK ; if so don't update the debouncer tm psport,#psmaks ; Test for program switch jr z,PRSWOLOSED ; if the switch is closed count up cp LEARNDB,#00 ; test for the non decrement point jr z, LEARNDBOK ; if at end skip dec dec LEARNDB ; jr LEARNBOK ; PHSWCLOSE: cp LEARNDB,#0FFH ; test for debouncer at max. jr z,LEARNDBOK ; if not at max increment inc LEARNDB ; increase the learn debounce timer LEARNDBOK AUX OBSTRUCTION OUTPUT AND LIGHT FUNCTION AUXLIGHT: test_light_on: cp LIGHT_FLAG,#LIGHT ; jr z,ado_light ; cp LIGHT1s,#00 ; test for no flash jr z,NO1S ; if not skip cp LIGHTS,#1 ; test for timeout jr nz,NO1S ; if not skip xor p0,#WORKLIGHT ; toggle light clr LIGHT1S ; onesnoted NO1S: cp FLASH_FLAG,#FLASH jr nz,dec_light ; clr VACFLASH ; Keep the vacation flash timer off dec FLASH_DELAY ; 250 ms period jr nz,dec_light ; cp STATUS, #RSSTATUS ; Test for in RS232 mode jr z,BlinkDone ; If so, don't blink the LED ; Toggle the wall control LED cp STATUS, #WALLOFF ; See if the LED is off or on jr z, TurnItOn ; TurnItOff: ld STATUS, #WALLOFF ; Turn the light off jr BlinkDone ; TurnItOn: ld STATUS, #CHARGE ; Turn the light on ld SWITCH_DELAY, #CMD_DEL_EX ; Reset the delay time for charge BlinkDone: ld FLASH_DELAY,#FLASH_TIME dec FLASH_COUNTER ; jr nz,dec light clr FLASH_FLAG ; dec_light: cp LIGHT_TIMER_HI,#0FFH ; test for the timer ignore jr z,exit_light ; if set then ignore tm T0EXT, #00010000b ; Decrement the light every 8 ms jr nz,exit_light ; (Use T0Ext to prescale) decw LIGHT_TIMER ; jr nz,exit_light ; if timer 0 turn off the light and p0,#˜LIGHT_ON ; turn off the light cp L_A_C, #00 ; Test for in a learn mode jr z, exit_light ; If not, leave the LED alone clr L_A_C ; Leave the learn mode or ledport,#ledh ; turn off the LED for program mode exit_light: ret ; return MOTOR STATE MACHINE STATEMACHINE: cp MOTDEL, #0FFH ; Test for max. motor delay jr z, MOTDELDONE ; if do, don't increment inc MOTDEL ; update the motor delay MOTDELDONE: xor p2,#FALSEIR ; toggle aux output cp DOG2,#8 ; test the 2nd watchdog for problem jp ugt,START ; if problem reset cp STATE,#6 ; test for legal number jp ugt,start ; if not the reset jp z,stop ; step motor 6 cp STATE,#3 ; test for legal number jp z,start ; if not the reset cp STATE,#0 ; test for autorev jp z,auto_rev ; auto reversing 0 cp STATE,#1 ; test for up jp z,up_direction ; door is going up 1 cp STATE,#2 ; test for autorev jp z,up_position ; door is up 2 cp STATE,#4 ; test for autorev jp z,dn_direction ; door is going down 4 jp dn_position ; door is down 5 AUTO_REV ROUTINE auto_rev: cp FAREVFLAG,#088H ; test for the forced up flag jr nz,LEAVEREV and p0,#LOW(˜WORKIGHT) ; turn off light clr FAREVFLAG ; one shot temp test LEAVEREV: cp MOTDEL, #10 ; Test for 40 ms passed jr ult, AREVON ; If not, keep the relay on AREVOFF: and p0,#LOW(˜MOTOR_UP & ˜MOTOR_DN) ; disable motor AREVON WDT ; kick the dog call HOLDREV ; hold off the force reverse ld LIGHT_FLAG,#LIGHT ; force the Light on no blink di dec AUTO_DELAY ; wait for .5 second dec BAUTO_DELAY ; wait for .5 second ei jr nz,arswitch ; test switches or p2,#FALSEIR ; set aux output for FEMA ;LOOK FOR LIMIT HERE (No) ld REASON,#40H ; set the reason for the change cp L_A_C, #075H ; Check for learning limits, jp nz, SET_UP_NOBLINK ; If not, proceed normally ld L_A_C, #076H ; jp SET_UP_NOBLINK ; set the state arswitch: ld REASON,#00H ; set the reason to command di cp SW_DATA,#CMD_SW ; test for a command clr SW_DATA ei jp z,SET_STOP_STATE ; if so then stop ld REASON,#10H ; set the reason as radio command cp RADIO_CMD,#0AAH ; test for a radio command jp z,SET_STOP_STATE ; if so the stop exit_auto_rev: ret ; return HOLDFREV: ld RPMONES,#244 ; set the hold off ld RPMCLEAR,#122 ; clear rpm reverse .5 sec di clr RPM_COUNT ; start with a count of 0 clr BRPM_COUNT ; start with a count of 0 ei ret DOOR GOING UP up_direction: WDT ; kick the dog cp OnePass, STATE ; Test for the memory read one-shot jr z, UpReady ; If so, continue ret ; Else wait UpReady: call HOLDFREV ; hold off the force reverse ld LIGHT_FLAG,#LIGHT ; force the light on no blink and p0,#LOW ˜MOTOR_DN ; disable down relay or p0,#LIGHT ON ; turn on the light cp MOTDEL,#10 ; test for 40 milliseconds jr ule,UPOFF ; if not timed CheckUpBlink: and P2M_SHADOW, #˜BLINK_PIN ; Turn on the blink output ld P2M, P2M_SHADOW ; cr P2, #BLINK_PIN ; Turn on the blinker decw BLINK ; Decrement blink time tm BLINK_HI, #10000000b ; Test for pre-travel blinking done jp z, NotUpSlow ; If not, delay normal motor travel UPON: or p0,#(MOTOR_UP | LIGHT_ON) ; turn on the motor and light UPOFF: cp FORCE_IGNORE,#1 ; test fro the end of the force ignore jr nz,SKIPUPRPM ; if not donot test rpmcount cp RPM_ACCOUNT,#12h ; test for less the 2 pulses jr ugt,SKIPUPRPM ; ld FAULTCODE,#05h SKIPUPRPM: cp FORCE_IGNORE, #00 ; test timer for done jr nz,test_UP_SW_pre ; if timer not up do not test force TEST_UP_FORCE: di dec RPM_TIME_OUT ; decrease the timeout dec BRPM_TIME_OUT ; decrease the timeout ei jr z,failed_up_rpm cp RampFlag, #RAMPUP ; Check for ramping up the force jr z, test_up_sw ; If not, always do full force check TestUpForcePot: di ; turn off the interrupt cp RPM_PERIOD_HI, UP_FORCE_HI ; Test the RPM against the force setting jr ugt, failed_up rpm ; jr ult, test_up_sw ; cp RPM_PERIOD_LO, UP_FORCE_LO ; jr ult, test_up_sw ; failed_up_rpm: ld REASON, #20H ; set the reason as force cp L_A_C, #076H ; If we're learning limits, jp nz, SET_STOP_STATE ; then set the flag to store ld L_A_C, #077H ; jp SET_STOP_STATE test_up_sw_pre: di dec FORCE_IGNORE dec BFORCE_IGNORE test_up_sw: di ld LIM_TEST_HI, POSITION_HI ; Calculate the distance from the up limit ld LIM_TEST_LO, POSITION_LO ; sub LIM_TEST_LO, UP_LIMIT_LO ; sbc LIM_TEST_HI, UP_LIMIT_HI ; cp POSITION_HI, #0B0H ; Test for lost door jr ugt, UpPosKnown ; If not lost, limit test is done cp POSITION_HI, #050H jr ult, UpPosKnown ; ei ; UPosUnknown: sub LIM_TEST_LO, #062H ; Calculate the total travel distance allowed sbc LIM_TEST_HI, #07FH ; from the floor when lost add LIM_TEST_LO, DN_LIMIT_LO ; adc LIM_TEST_HI, DN_LIMIT_HI ; UpPosKnown: ; ei ; cp L_A_C, #070H ; If we're positioning the door, forget the limit jr z, test_up_time ; and the wall control and radio cp LIM_TEST_HI, #11 ; Test for exactly at the limit jr nz, TestForPastUp ; If not, see if we've passed the limit cp LIM_TEST_LO, #00 ; jr z, AtUpLimit ; TestForPastUp: tm LIM_TEST HI, #10000000b ; Test for a negative result (past the limit, but close) jr z, get_sw ; If so, set the limit AtUpLimit: ld REASON,#50H ; set the reason as limit cp L_A_C, #072H ; If we're re-learning limits, jr z, ReLearnLim ; jump cp L_A_C, #076H ; If we're learning limits, jp nz, SET_UP_POS_STATE ; then set the flag to store ld L_A_C, #077H ; jp SET_UP_POS_STATE ; ReLearnLim: ld L_A_C, #073H ; jp SET_UP_POS_STATE ; get_sw: cp L_A_C, #070H ; Test for positioning the up limit jr z,NotUpSlow ; If so, don't slow down TestUpSlow: cp LIM_TEST_HI, #HIGH(UPSLOWSTART) ; Test for start of slowdown jr nz, NotUpSlow ; (Cheating -- the high byte of the number is zero) cp LIM_TEST_LO, #LOW(UPSLOWSTART) ; jr ugt, NotUpSlow ; UpSlow: ld RampFlag, #RAMPDOWN ; Set the slowdown flag NotUpSlow: ld REASON, #10H ; set the radio command reason cp RADIO_CMD,#0AAH ; test for a radio command jp z,SET_STOP_STATE ; if so stop ld REASON,#00H ; set the reason as a command di cp SW_DATA, #CMD_SW ; test for a command condition clr SW_DATA ei jr ne,test_up_time ; jp SET_STOP_STATE ; test_up_time: ld REASON,#70H ; set the reason as a time out decw MOTOR_TIMER ; decrement motor timer jp z,SET_STOP_STATE ; exit_up_dir: ret ; return to caller DOOR UP up_position: WDT ; kick the dog cp FAREVFLAG,#088E ; test for the forced up flag jr nz, LEAVELIGHT and p0,#LOW(˜WORKLIGHT) ; turn off light jr UPNOFLASH ; skip clearing the flash flag LEAVELIGHT: ld LIGHT_FLAG,#00H ; allow blink UPNOFLASH: cp MOTDEL, #10 ; Test for 40 ms passed jr ult, UPLIMON ; If not, keep the relay on UPLIMOFF: and p0,#LOW(˜MOTOR UP & ˜MOTOR_DN) ; disable motor UPLIMON: cp L_A_C, #073H ; If we've begun the learn limits cycle, jr z,LAOUPPOS ; then delay before traveling cp SW_DATA, #LIGHT_SW ; light sw debounced jr z,work_up ; ld REASON, #10H ; set the reason as a radio command cp RADIO_CMD,#0AAH ; test for a radio cmd jr z,SETDNDIRSTATE ; if so start down ld REASON, #00H ; set the reason as a command di cp SW_DATA,#CMD_SW ; command sw debounced? clr SW_DATA ei jr z,SETDNDIRSTATE ; if command ret SETDNDIRSTATE: ld ONEP2,#10 ; set the 1.2 sec timer jp SET_DN_DIR_STATE LACUPPOS: cp MOTOR_TIMER_HI, #HIGH(LACTIME) ; Make sure we're set to the proper time jr ule, UpTimeOx ld MOTOR_TIMER_HI, #HIHG(LACTIME) ld MOTOR_TIMER_LO, #LOW(LACTIME) UpTimeOk: decw MOTOR_TIMER ; Count down more time jr nz, up_pos_ret ; If nor timed out, leave StartLACDown: ld L_A_C, #074H ; Set state as traveling down in LAC clr UP_LIMIT_HI ; Clear the up limit clr UP_LIMIT_LO ; and the position for clr POSITION_HI ; determining the new up clr POSITION_LO ; limit of travel ld PassCounter, #030H ; Set pass points at max. jp SET_DN_DIR_STATE ; Start door traveling down work_up: xor p0,#WORKLIGHT ; toggle work light ld LIGHT_TIMER_HI,#0FFH ; set the timer ignore and SW_DATA, #LOW(˜LIGHT_SW) ; Clear the worklight bit up_pos_ret: ret ; return DOOR GOING DOWN dn_direction: WDT ; kick the dog cp OnePass, STATE ; Test for the memory read one-shot jr z, DownReady ; If so, continue ret ; else wait DownReady: call HOLDFREV ; hold off the force reverse clr FLASH_FLAG ; turn off the flash ld LIGHT_FLAG,#LIGHT ; force the light on no blink and p0,#LOW(˜MOTOR_UP ; turn off motor up or p0,#LIGHT_ON ; turn on the light cp MOTDEL,#10 ; test for 40 milliseconds jr ule,DNOFF ; if not timed CheckDnBlink: and P2M_SHADOW, #˜BLINK_PIN ; Turn on the blink output ld P2M, P2M_SHADOW ; or P2, #BLINK_PIN ; Turn on the blinker decw BLINK ; Decrement blink time tm BLINK_HI, #10000000b ; Test for pre-travel blink done jr z, NotOnSlow ; If not, don't start the motor DNON: or p0,#(MOTOR_DN LIGHT_ON ; turn on the motor and Light DNOFF: cp FORCE_IGNORE,#01 ; test fro the end of the force ignore jr nz,SKIPDNRPM ; if not donot test rpmcount cp RPM_ACOUNT,·02H ; test for less the 2 pulses jr ugt,SKIPDNRPM ; ld FAULTCODE,#05h SKIPDNRPM: cp FORCE_IGNORE,#00 ; test timer for done jr nz,test_on_sw_pre ; if timer not up do not test force TEST_DOWN_FORCE: di dec RPM_TIME_OUT ; decrease the timeout dec BRPM_TIME_OUT ; decrease the timeout ei jr z,failed_dn_rpm cp RampFlag, #RAMPUP ; Check for ramping up the force jr z, test_dn_sw ; If not, always do full force check TestDownForcePot: di ; turn off the interrupt cp RPM_PERIOD_HI, DN_FORCE_HI ; rest the RPM against the force setting jr ugt, failed_dn_rpm ; if too slow then force reverse jr ult, test_dn_sw ; if faster then we're fine cp RPM_PERIOD_LO, DN_FORCE_LO ; jr ult, test_dn_sw ; failed_dn_rpm: +T+L,12 cp L_A_C, #074H ; Test for learning limits jp z, DnLearnRev ; If not, set the state normally tm POSITION_HI, #11000000b ; Test for below last pass point jr nz, DnRPMRev ; if not, we're nowhere near the limit tm LIM_TEST_HI, #10000000b ; Test for beyond the down limit jr nz, DoDownLimit ; If so, we've driven into the down limit DnRPMRev: ld REASON, #0B0H ; set the reason as force cp POSITION_HI, #0B0H ; Test for lost, jp ugt, SET_AREV_STATE ; if not, autoreverse normally cp POSITION_HI, #050H ; jp ult, SET_AREV_STATE ; di ; Disable interrupts ld POSITION_HI, #07FH ; Reset lost position for max. travel up ld POSITION_LO, #080H ; ei ; Re-enable interrupts jp SET_AREV_STATE ; DnLearnRev: ld L_A_C, #075H ; Set proper LAC jp SET_AREV_STATE ; test_dn_sw_pre: di dec FORCE_IGNORE dec BFORCE_IGNORE test_dn_sw: di ; cp POSITION_HI, #050H ; Test for lost in mid travel jr ult, TestDnLimGood ; cp POSITION_HI, #0B0H ; If so, don't test for limit until jr ult, NotDnSlow ; a proper pass point is seen TestDnLimGood: ld LIM_TEST_HI, DN_LIMIT_HI ; Measure the distance to the down limit ld LIM_TEST_LO, DN_LIMIT_LO ; sub LIM_TEST_LO, POSITION_LO ; sbc LIM_TEST_HI, POSITION_HI ; ei ; cp L_A_C, #070H If we're in the learn cycle, forget the limit jr uge, test_dn_time ; and ignore the radio and wall control tm LIM_TEST_HI, #10000000b ; Test for a ngegative result (past the down limit) jr z, call_sw_dn ; If so, set the limit cp LIM_TEST_LO, #255 36 ; Test for 36 pulses (3″) beyond the limit jr ugt, NotDnSlow ; if not, then keep driving onto the floor DoDownLimit: ld REASON,#50H ; set toe reason as a limit cp CMD_DEB,#0FFH ; test for the switch still held jr nz,TESTRADIO ; ld REASON,#90H ; closed with the control held jr TESTFORCEIG TESTRADIO: cp LAST_CMD,#00 ; test for the last command being radio jr nz,TESTFORCEIG ; if not test force cp CodeFlag,#BRECEIVED ; test for the b code flag jr nz,TESTFORCEIG ; ld REASON,#0A0H ; set the reason as b code to limit TESTFORCEIG: cp FORCE_IGNORE,#00H ; test the force ignore for done jr z,NOAREVDN ; a rev if limit before force enabled ld REASON,#60h ; early limit jp SET_AREV_STATE ; set autoreverse NOAREVDN: and p0,#LOW(˜MOTOR_DN ; jp SET_DN_POS_STATE ; set the state call_sw_dn: cp LIM_TEST_HI, #HIGH(DNSLOWSTART, ; Test for start of slowdown jr nz, NotDnSlow ; (Cheating -- the high byte is zero) cp LIM_TEST_LO, #LOW(DNSLOWSTART) ; jr ugt, NotDnSlow ; DnSlow: ld RampFlag, #RAMPDOWN ; Set the slowdown flag NotDnSlow: ld REASON, #10H ; set the reason as radio command cp RADIO_CMD,#0AAH ; test for a radio command jp z,SET_AREV_STATE ; if so arev ld REASON,#00H ; set the reason as command di cp SW_DATA,#CMD_SW ; test for command clr SW_DATA ei jp z,SET_AREV_STATE ; test_dn_time: ld REASON,#70H ; set the reason as timeout decw MOTOR_TIMER ; decrement motor timer jp z,SET_AREV_STATE ; test_obs_count: cp OBS_COUNT,#00 ; Test the obs count jr nz, exit_dn_dir ; if not done, don't reverse cp FORCE_IGNORE, #(ONE_SEC / 2) ; Test for 0.5 second passed jr ugt, exit_an_dir ; if within first 0.5 sec, ignore it cp LAST_CMD,#00 ; test for the last command from radio jr z,OBSTESTB ; if last command was a radio test b cp CMD_DEB,#0FFH ; test for the command switch holding jr nz,OBSAREV ; if the command switch is not holding ; do the autorev jr exit_dn_dir ; otherwise skip OBSAREV: ld FLASH_FLAG, #0FFH ; set flag ld FLASH_COUNTER, #20 ; set for 10 flashes ld FLASH_DELAY,#FLASH_TIME ; set for .5 Hz period ld REASON,#30E ; set the reason as autoreverse jp SET_AREV_STATE ; OBSTESTB: cp CodeFlag,#BRECEIVED ; test for the b code flag jr nz,OBSAREV ; if not b code then arev exit_dn_dir: ret ; return DOOR DOWN dn_position: WDT ; kick the doa cp FAREVFLAG,#088H ; test for the forced up flag jr nz,DNLEAVEL ; and p0,#LOW(˜WORKLIGHT) ; turn off light jr DNNOFLASH ; skip clearing the flash flag DNLEAVEL: ld LIGHT_FLAG,#00H ; allow blink DNNOFLASH: cp MOTDEL, #10 ; Test for 40 ms passed jr ult, DNLIMON ; If not, keep the relay on DNLIMOFF: and p0,#LOW(˜MOTOR_UP & ˜MOTOR_DN) ; disable motor DNLIMON: cp SW_DATA,#LIGHT_SW ; debounced? light jr z,work_an ; ld REASON,#10H ; set the reason as a radio command cp RADIO_CMD,#0AAH ; test for a radio command jr z,SETUPDIRSTATE ; if so go up ld REASON,#00H ; set the reason as a command di cp SW_DATA,#CMD_SW ; command sw pressed? clr SW_DATA ei jr z,SETUPDIRSTATE ; if so go up ret SETUPDIRSTATE: ld ONEP2,#10 ; set the 1.2 sec timer jp SET_UP_DIR_STATE work_dn: xor p0,#WORKLIGMT ; toggle work light ld LIGHT_TIMER_HI,#0FFH ; set the timer ignore and SW_DATA, #LOW(˜LIGHT_SW) ; Clear the worklight bit dn_pos_ret: ret ; return STOP stop: WDT ; kick the dog cp FAREVFLAG,#066H ; test for the forced up flag jr nz,LEAVESTOP and p0,#LOW˜WORKLIGHT ; turn off light jr STOPNOFLASH ; LEAVESTOP: ld LIGHT_FLAG,#00H ; allow blink STOPNOFLASH: cp MOTDEL, #10 ; Test for 40 ms passed jr ult, STOPMIDON ; If not, keep the relay on STOPMIDOFF: and p0,#LOW(˜MOTOR_UP & ˜MOTOR_DN ; disable motor STOPMIDON: cp SW_DATA,#LIGHT_SW ; debounced? light jr z,work_stop ; ld REASON,#10H ; set the reason as radio command cp RADIO_CMD,#0AAH ; test for a radio command jp z,SET_DN_DIR_STATE ; if so go down ld REASON,#00H ; set the reason as a command di cp SW_DATA,#CMD_SW ; command sw pressed? clr SW_DATA ei jp z,SET_DN_DIR_STATE ; if so go down ret work_stop: xor p0,#WORKLIGHT ; toggle work light ld LIGHT_TIMER_HI,#0FFH ; set the timer ignore and SW_DATA, #LOW,˜LIDHT_SW ; Clear the worklight bit stop_ret: ret ; return SET THE AUTOREV STATE SET_ARVE_STATE: di ; cp L_A_C, #070H ; Test for learning limits, jt uge, LearningRev ; If not, do a normal autoreverse cp POSITION_HI, #020H ; Look for lost postion jr ult, DoTheArev ; If not, Proceed as normal cp POSITION_HI, #0D0H ; Look for lost position jr ugt, DoTheArev ; If not, proceed as normal ;Otherwise, we're list -- ignore commands cp REASON, #020H Don't respond to command or radio jf uge, DoTheArev ; clr RADIO_CMD ; Throw out the radio command ei ; Otherwise, just ignore it ret ; DoTheArev: ld STATE,#AUTO_REV ; if we got here, then reverse motor ld RampFlag, #STILL ; Set the FET's to off clr PowerLevel ; jr SET_ANY ; Done LearningRev: ld STATE,#AUTO_REV ; if we got here, then reverse motor ld RampFlag, #STILL ; Set the FET's to off clr PowerLevel ; cp L_A_C, #075H ; Check for proper reversal jr nz, ErrorLearnArev ; If not, stop the learn cycle cp PassCounter, #030H ; If we haven't seen a pass point, jr z, ErrorLearnArev ; then flag an error GoodLearnArev: cp POSITION_HI, #00 ; Test for down limit at least jr nz, DnLimGood ; 20 pulses away from pass point cp POSITION_LO, #20 ; jr ult, MovePassPoint ; If not, use the upper pass point DnLimGood: and PassCounter, #10100000b ; Set at lowest pass point GotDnLim: di ld DN_LIMIT_HI, POSITION_HI ; Set the new down limit ld DN_LIMIT_LO, POSITION_LO ; add DN_LIMIT_LO, #01 ; Add in a pulse to guarantee reversal off the block adc DN_LIMIT_HI, #00 ; jr SET_ANY ; ErrorLearnArev: ld L_A_C, #071H ; Set the error in learning state jr SET_ANY MovePassPoint: cp PassCounter, #02FH ; If we have only one pass point, jr z, ErrorLearnArev ; don't allow it to be this close to the floor di add POSITION_LO, #LOW(PPOINTPULSES) ; Use the next pass point up adc POSITION_HI, #HIGH(PPOINTPULSES) ; add UP_LIMIT_LO, #LOW PPOINPULSES ; adc UP_LIMIT_HI, #HIGH PPOINTPULSES ; ei ; or PassCounter, #01111111b ; Set pass counter at −1 jr GotDmLim ; SET THE STOPPED STATE SET_STOP_STATE: di cp L_A_C, #070H ; If we're in the learn mode, jr uge, DoTheStop ; Then don't ignore anything cp POSITION_HI, #020H ; Look for lost postion jr ult, DoTheStop ; If not, proceed as normal cp POSITION_HI, #0D0H ; Look for lost postion jr ugt, DoTheStop ; If not, proceed as normal ;Otherwtse, we're lost -- ignore commands cp REASON, #020H ; Don't respond to command or radio jr uge, DoTheStop ; clr RADIO_CMD ; Throw out the radio command ei ; Otherwise, just ignore it ret DoTheStop: ld STATE,#STOP ; ld RampFlag, #STILL ; Stop the motor at the FET's clr PowerLevel ; jr SET_ANY SET THE DOWN DIRECTION STATE SET_DN_DIR_STATE: ld BLINK_HI, #0FFH ;Initially disable pre-travel blink call LookForFlasher ;Test to see if flasher present tm P2, #BLINK PIN ;If the flasher is not present, jr nz, SET_DN_NOBLINK ;don't flash it ld BLINK_LO, #0FFH ;Turn on the blink timer ld BLINK_HI, #01H ; SET_DN_NOBLINK: di ld RampFlag, #RAMPUP ; Set the flag to accelerate motor ld PowerLevel, #4 ; Set speed at minimum ld STATE,#DN_DIRECTION ; energize door clr FAREVFLAG ; one shot the forced reverse cp L_A_C, #070H ; If we're learning the limits, jr uge, SET_ANY ; Then don't bother with testing anything cp POSITION_HI, #020H ; Look for lost postion jp ult, SET_ANY ; If not, proceed as normal cp POSITION_HI, #0D0H ; Lock for lost postion jp ugt, SET_ANY ; If not, proceed as normal LostDn: cp FirstRun, #0C ; If this isn't our first operation when lost, jr nz, SET_ANY ; then ALWAYS head down tm PassCounter, #01111111b ; If we are below the lowest jr z, SET_UP_DIR_STATE ; pass point, head up to see it tcm PassCounter, #01111111b ; If our pass point number is set at −1, jr z, SET_UP_DIR_STATE ; then go up to find the position jr SET_ANY ; Otherwise, proceed normally SET THE DOWN POSITION STATE SET_DN_POS_STATE: di ld STATE,#DN_POSITION ; load new state ld RampFlag, #STILL ; Stop the motor at the FET's clr PowerLevel ; jr SET_ANY SET THE UP DIRECTION STATE SET_UP_DIR_STATE: ld BLINK_HI, #0FFH ;Initially turn off blink call LookForFlasher ;Test to see if flasher present tm P2, #BLINK_PIN ;If the flasher is not present, jr nz, SET_UP_NOBLINK ;don't flash it ld BLINK_LO, #0FFH ;Turn on the blink timer ld BLINK_HI, #01H ; SET_UP_NOBLINK di ld RampFlag,#RAMPUP ; Set the flag to accelerate to max. ld PowerLevel, #4 ; Start speed at minimum ld STATE, #UP_DIRECTION ; jr SET_ANY ; SET THE UP POSITION STATE SET_UP_POS_STATE: di ld STATE,#UP_POSITION ; ld RampFlag, #STILL ; Stop the motor at the FET's clr PowerLevel ; SET ANY STATE SET_ANY: and P2M_SHADOW, #˜BLINK_PIN ; Turn on the blink output ld P2M, P2M_SHADOW : and P2, #˜BLINK_PIN ; Turn off the light cp PPOINT_DEB, #2 ; Test for pass point being seen jr ult, NoPrePPoint ; If signal is low, none seen PrePPoint: or PassCounter, #0000000b ; Flag pass point signal high jr PrePPointDone ; NoPrePPoint: and PassCounter, #01111111b ; Flag pass point signal low PrePPointDone: ld FirstRun, #0FFH ; One-shot the first run flag DONE IN MAIN ld BSTATE,STATE ; set the backup state di clr RPM_COUNT ; clear the rpm counter clr BRPM_COUNT ; ld AUTO_DELAY,#AUTO_REV_TIME ; set the .5 second auto rev timer ld BAUTO_DELAY, #AUTO_REV_TIME ; ld FORCE_IGNORE,#ONE_SEC ; set the force ignore timer to one sec ld BFORCE_IGNORE,#ONE_SEC ; set the force ignore timer to one sec ld RPM_PERIOD_HI, #0FFH ; Set the RPM period to max. to start ei ; Flush out any pending interrupts di ; cp L_A_C, #070H ; If we are in learn mode, jr uge, LearnModeMotor ; don't test the travel distance push LIM_TEST_HI ; Save the limit tests push LIM_TEST_LO ; ld LIM_TEST_HI, DN_LIMIT_HI +T+L,28 Test the door travel distance to ld LIM_TEST_LO, DN_LIMIT_LO ; see if we are shorter than 2.3M sub LIM_TEST_L0, UP_LIMIT_LO ; sbc LIM_TEST_HI, UP_LIMIT_HI ; cp LIM_TEST_HI, #HIGH(SHORTDOOR) ; If we are shorter than 2.3M, jr ugt, DoorIsNorm ; then set the max. travel speed to 2/3 jr ult, DoorIsShort ; Else, normal speed cp LIM_TEST_LO, #LOW(SHORTDOOR) ; jr ugt, DoorIsNorm ; DoorIsShort: ld MaxSpeed, #12 ; Set the max. speed to 2/3 jr DoorSet ; DoorIsNorm: ld MaxSpeed, #20 ; DoorSet: pop LIM_TEST_LO ; Restore the limit tests pop LIM_TEST HI ; ld MOTOR_TIMER_HI,#HIGH(MOTORTIME) ld MOTOR_TIMER_LO,#LOW(MOTORTIME) MotorTimeSet: ei clr RADIO_CMD ; one shot clr RPM_ACOUNT ; clear the rpm active counter ld STACKREASON,REASON ; save the temp reason ld STACKFLAG,#0FFH ; set the flag TURN_ON_LIGHT: call SetVarLight ; Set the worklight to the proper value tm P0, *LIGHT_ON ; If the light is on skip clearing jr nz,lighton ; lightoff: clr MOTDEL ; clear the motor delay lighton: ret LearnModeMotor: ld MaxSpeed, #12 ; Default to slower max. speed ld MOTOR_TIMER_HI,#HIGH(LEARNTIME) ld MOTOR_TIMER_LO,#LOW(LEARNTIME) jr MotorTimeSet ; Set door to longer run for learn THIS IS THE MOTOR RPM INTERRUPT ROUTINE RPM: push rp ; save current pointer srp #RPM_GROUP ;point to these reg ld rpm_temp_of,T0_0FLOW ; Read the 2nd extension ld rpm_temp_hi,T0EXT ; read the timer extension ld rpm_temp_lo,T0 ; read the timer tm IRQ,#00010000B ; test for a pending interrupt jr z,RPMTIMEOK ; if not then time ok RPMTIMEERROR: tm rpm_temp_lo, #10000000B ; test for timer reload jr z,RPMTIMEOK ; if no reload time is ok decw rpm_temp_hiword ; if reloaded then dec the hi to resync RPMTIMEOK: cp RPM_FILTER, #128 ; Signal must have been high for 3 ms before jr ult, RejectTheRPM ; the pulse is considered legal tm P3, #00000010B ; If the line is sitting high, jr nz, RejectTheRPM ; then the falling edge was a noise pulse RPMIsGood: and imr,#11111011b ; turn off the interupt for up to 500uS ld divcounter, #03 ; Set to divide by 8 (destroys value in RPM_FILTER) DivideRPMLoop: rcf ; Reset the carry rrc rpm_temp_of ; Divide the number by 8 so that rrc rpm_temp hi ; it will always fit within 16 bits rrc rpm_temp_lo ; djnz divcounter, DivideRPMLoop ; Loop three times (Note: This clears RPM FILTER) ld rpm_period_lo, rpm_past_lo ; ld rpm_period_hi, rpm_past_hi ; sub rpm_period_lo, rpm_temp_lo ; find the period of the last pulse sbc rpm_period_hi, rpm_temp_hi ; ld rpm_past_lo, rpm_temp_lo ; Store the current time for the ld rpm_past_hi, rpm_temp_hi ; next edge capture cp rpm_period_hi,#12 ; test for a period of at least 6.144mS jr ult,SKIPC ; if the period is less then skip counting TULS: INCRPM: inc RPM_COUNT ; increase the rpm count inc BRPM_COUNT ; increase the rpm count SKIFC: inc RPM_ACOUNT ; increase the rpm count cp RampFlag, #RAMPUP ; If we're ramping the speed up, jr z, MaxTimeOut ; then set the timeout at max. cp STATE, #DN_DIRECTION ; If we're traveling down, jr z, DownTimeOut ; then set the timeout from the down force UpTimeOut: ld rpm_time_out,UP_FORCE_HI ; Set the RPM timeout to be equal to the up force setting rcf ; Divide by two to account rrc rpm_time_out ; for the different prescalers add rpm_time_out, #2 ; Round up and account for free-running prescale jr GotTimeOut MaxTimeOut: ld rpm_time_out, #125 ; Set the RPM timeout to be 500mns jr GotTimeOut ; DownTimeOut: ld rpm_time_out,DN_FORCE_HI ; Set the RPM timeout to be equal to the down force setting rcf ; Divide by two to account rrc rpm_time_out ; for the different prescalers add rpm_time_out, #2 ; Round up and account for free-running prescale GotTimeOut: ld BRPM_TIME_OUT, rpm_time_out ; Set the backup to the same value ei Position Counter Position is incremented when going down and decremented when going up. The zero position is taken to be the upper edge of the pass point signal (i.e. the falling edge in the up direction, the rising edge in the down direction) cp STATE, #UP_DIRECTION ; Test for the proper direction of the counter jr z, DecPos ; cp STATE, #STOP ; jr z, DecPos ; +T+L,12 cp STATE, #UP_POSITION ; jr z, DecPos ; IncPos: incw POSITION ; cp PPOINT_DEB, #2 ; Test for pass point being seen jr ult, NoDNPPoint ; If signal is low, none seen DnPPoint: or PassCounter, #10000000b +T+L,32 ; Mark pass point as currently high jr CtrDone ; NoDnPPoint: tm PassCounter, #10000000b ; Test for pass point seen before jr z, PastDnEdge ; If not, then we're past the edge AtDnEdge cp L_A_C, #074H ; Test for learning limits jr nz, NormalDownEdge ; if not, treat normally LearnDownEdge: di sub UP_LIMIT_LO, POSITION_LO ; Set the up position higher sbc UP_LIMIT_HI, POSTION_HI ; dec PassCounter ; Count pass point as being seen jr Lowest1 ; Clear the position counter NormalDownEdge: dec PassCounter ; Mark as one pass point closer to floor tm PassCounter, #01111111b ; Test for lowest pass point jr nz, NotLowest1 ; If not, don't zero the position counter Lowest1: di clr POSITION_HI ; Set the position counter back to zero ld POSITION_LO, #1 ; ei ; NotLowest1: cp STATUS, #PSSTATUS ; Test for in R5232 mode jr z, DontResetWall3 ; If so, don't blink the LED ld STATUS, #WALLOFF ; Blink the LED for pass point clr VACFLASH ; Set the turn-off timer DontResetWall3. PastDnEdge: NoUpPPoint: and PassCounter, #01111111b ; Clear the flag for pass point high jr CtrDone ; DecPos: decw POSITION ; cp PPOINT_DEB, #2 ; Test for pass point being seen jr ult, NoUpPPoint ; If signal is low, none seen UpPPoint: tm PassCounter, #10000000b +T+L,32 ; Test for pass point seen before jr nz, PastUpEdge ; If so, then we're past the edge AtUpEdge: tm PassCounter, #01111111b ; Test for lowest pass point jr nz, NotLowest2 ; If not, don't zero the position counter Lowest2: di clr POSITION_HI ; Set the position counter back to zero clr POSITION_LO ; ei NotLowest2: cp STATUS, #RSSTATUS ; Test for in R5232 mode jr z, DontResetWall2 ; If so, don't blink the LED ld STATUS, #WALLOFF ; Blink the LED for pass point clr VACFLASH ; Set the turn-off timer DontResetWall2: inc PassCounter ; Mark as one pass point higher above cp PassCounter, FirstRun ; Test for pass point above max. value jr ule, PastUpEdge ; If not, we're fine ld PassCounter, FirstRun ; Otherwise, correct the pass counter PastUpEdge: or PassCounter, #10000000b ; Set the flag for pass point high before CtrDone: RejectTheRPM: pop rp ; return the rp iret ; return THIS IS THE SWITCH TEST SUBROUTINE STATUS 0 => COMMAND TEST 1 => WORKLIGHT TEST 2 => VACATION TEST 3 => CHARGE 4 => RSSTATUS -- RS232 mode, don't scan for switches 5 => WALLOFF -- Turn off the wall control LED SWITCH DATA 0 => OPEN 1 => COMMAND CMD_SW 2 => WORKLIGET LIGHT_SW 4 => VACATION VAC_SW switches: ei 4-22-97 CP LIGHT_DEB,#0FFH ;is the light button being held? JR NZ,NotHeldDown ;if not debounced, skip long hold CP EnableWorkLight,#01100000B ;has the 10 sec. already passed? JR GE,HeldDon CP EnableWorkLight,#01010000B JR LT,HeldDown LD EnableWorkLight,#10010000B ;when debounce occurs, set register ;to initiate e2 write in mainloop JR HeldDown NotHeldDown: CLR EnableWorkLight HeldDown: and SW_DATA, #LIGHT_SW ; Clear all switches excect for worklrght cp STATUS, #WALLOFF ; Test for illegal status jp ugt, start ; if so reset jr z, NoWallCtrl ; Turn off wall control state cp STATUS, #RSSTATUS ; Check for in RS232 mode jr z, NOTFLASHED ; If so, skip the state machine cp STATUS,#3 ; test for illegal number jp z,charge ; if it is 3 then goto charge cp STATUS,#2 ; test for vacation jp z,VACATION_TEST ; if so then jump cp STATUS,#1 ; test for worklight jp z,WORKLIGHT_TEST ; if so then jump ; else it id command COMMAND_TEST: cp VACFLAC,#00H ; test for vacation mode jr z,COMMAND_TEST1 ; if not vacation skip flash inc VACFLASH ; increase the vacation flash timer cp VACFLASH,#10 ; test the vacation flash period jr ult,COMMMAND_TEST1 ; if lower period skip flash and p3,#˜CHARGE_SW ; turn off wall switch or p3,#DIS_SW ; enable discharoe cp VACFLASH,#60 ; test the time delay for max jr nz,NOTFLASHED ; if the flash is not done jump and ret clr VACFLASH ; restart the timer NOTFLASHED ret ; return NoWallCtrl: and P3, #˜CHARGE_SW ; Turn off the circuit or P3, #DIS_SW ; inc VACFLASH ; Update the off time cp VACFLASH, #81 ; If off tine hasn't expired, jr ult, KeepOff ; keep the LED off ld STATUS, #CHARGE ; Reset the wall control ld SWITCH_DELAY, #CMD_DEL_EX ; Reset the charce timer KeepOff: ret ; COMMAND_TEST1: tm p0,#SWTCHES1 ; command sw pressed? jr nz,CMDOPEN ; open command tm P0,#SWITCHES2 ; test the second command input jr nz,CMDOPEN CMDCLOSE1: ; closed command call DECVAC ; decrease vacation debounce call DECLIGHT ; decrease light debounce cp CMD_DEB,#0FFH ; test for the max number jr z,SKIPCMDINO ; if at the max skip inc di inc CMD_DEB ; increase the debouncer inc BCMD_DEB increase the debouncer SKIPCMDINC: cp CMD_DEB,#CMD_MAFE ; jr nz,CMDEXIT ; if not made then exit call CmdSet ; Set the command switch CMDEXIT: or p3,#CHARGE_SW ; turn on the charge system and p3,#˜DIS_SW ; ld SWITCH_DELAY,#CMD_DEL_EX ; set the delay time to 8mS ld STATUS,#CHARGE ; charge time CMDDELEXIT: ret ; CmdSet: cp L_A_C, #070H ; Test for in learn limits mode jr ult, RegCmdMake ; If not, treat as normal command jr ugt, LeaveLAC ; If learning, command button exits call SET_UP_NOBLINK ; Set the up direction state jr CMDMAXEDONE ; RegCmdMake: cp LEARNDB, #0FFH ; Test for learn button held jr z, GoIntoLAC ; If so, enter the learn mode NormalCmd: di ld LAST_CMD,#055H ; set the last command as command cmd: ld SW_DATA,#CMD_SW ; set the switch data as command cp AUXLEARNSW,#100 ; test the time jr ugt,SKIP_LEARN push RP srp #LEARNEE_GRP call SETLEARN ; set the learn mode dr SW_DATA ; clear the cmd pop RP or p0,#LIGHT_ON ; turn on the light call TURN_ON_LIGHT ; turn on the light CMDMAKEDONE: SKIP_LEARN: ld CMD_DEB,#0FFH ; set the debouncer to ff one shot ld BCMD_DEB,#0FFH ; set the debouncer to ff one shot ei ret LeaveLAC: clr L_A_C ; Exit the learn mode or ledport,#ledn ; turn off the LED for program mode call SET_STOP_STATE ; jr CMDMAKEDONE ; GoIntoLAC: ld L_A_C, #170H ; Start the learn limits mode clr FAULTCODE ; Clear any faults that exist clr CodeFlag ; Clear the reoular learn mode ld LEARNT, #0FFH ; Turn off the learn timer ld ERASET, #OFFH ; Turn off the erase timer jr CMDMAKEDONE ; CMDOPEN: ; command switch open and p3,#˜CHARGE_SW ; turn off charging sw or p3,#DIS_SW ; enable discharge ld DELAYC,#16 ; set the time delay DELLOOP: dec DELAYC jr nz,DELLOOP ; loop till delay is up tm p0,#SWITCHES1 ; command line still high jr nz,TESTWL ; if so return later call DECVAC ; if not open line dec all debouncers call DECLIGHT ; call DECCMD ; ld AUXLEARNSW,#0FfH ; turn off the aux learn switch jr CMDEXIT ; and exit TESTWL: ld STATUS,#WL_TEST ; set to test for a worklight ret ; return WORKLIGHT_TEST: tm p0,#SWITCHES ; command line still high jr nz,TESTVAC2 ; exit setting to test for vacation call DECVAC ; decrease the vacation debouncer call DECCMD ; and the command debouncer cp LIGHT_DEB,#0FFH ; test for the max jr z,SKIPLIGHTING ; if at the max skip inc inc LIGHT_DEB ; inc debouncer SKIPLIGHTING: cp LIGHT_DEB,#LIGHT_MAKE ; test for the light make jr nz,CMDEXIT ; if not then recharge delay call LightSet ; Set the light debouncer jr CMDEXIT ; then recharge LightSet: ld LIGHT_DEB,#0FFH ; set the debouncer to max ld SW_DATA,#LIGHT_SW ; set the data as worklight cp RRTO,#RDROPTIME ; test for code reception jr ugt,CMDEXT ; if not then skip the seting of flag clr AUXLEARNSW ; start the learn timer ret TESTVAC2: ld STATUS,#VAC_TEST ; set the next test as vacation ld switch_delay,#VAC_DEL ; set the delay LIGHTDELEXIT: ret ; return VACATION_TEST: djnz switch_delay,VACDELEXIT ; tm p0,#SWITCHES1 ; command line still high jr, EXIT_ERROR ; exit with a error setting open state call DECLIGHT ; decrease the light debouncer call DECCMD ; decrease the command debouncer cp VAC_DEB,#0FFH ; test for the max jr z,VACINCSKIP ; skip the incrementing VACINCSKIP: or VACFLAG,#00H ; test for vacation mode jr z,VACOUT ; if not vacation use out time VACIN: or VAC_DEB,#VAC_MAKE_IN ; test for the vacation make point jr nz,VACATION_EXIT ; exit of not made call VacSet ; jr VACATION_EXIT ; VACOUT: cp VAC_DEB,#VAC_MAKE_OUT ; test for the vacation make count jr nz,VACATION_EXIT ; exit if not made call VacSet ; jr VACATION_EXIT ; Forget vacatoon mode VacSet: ld VAC_DEB,#0FFH ; set vacation debouncer to max cp AUXLEARNSW,#100 ; test the time jr ugt,SKIP_LEARNV push RP srp #LEARNEE_GRP call SETLEARN ; set the learn mode pop RP cr p0, #LIGHT_ON ; Turn on the worklight call TURN_ON_LIGHT ; ret SKIP_LEARNT: ld VACCHANGE,#0AAH ; set the toggle data cp RRTO,#RDROPTIME ; test for code reception jr ugt,VACATION_EXIT ; if not then skip the seting of flag clr AUXLEARNSW ; start the learn timer VACATION_EXIT: ld SWITCH_DELAY,#VAC_DEL_EX ; set the delay ld STATUS,#CHARGE ; set the next test as charge VACDELEXIT: ret EXIT_ERROR: call DECCMD ; decrement the debouncers call DECVAC ; call DECLIGHT ; ld SWITCH_DELAY,#VAC_DEL_EX ; set the delay ld STATUS,#CHANGE ; set the next test as charge ret charge: or p3,#CHARGE_SW ; and p3,#˜DIS_SW ; dec SWITCH_DELAY ; jr nz,charge_ret ; ld STATUS,#CMD_TEST ; charge_ret: ret DECOMD: cp CMD_DEB,#00H ; test for the min number jr z,SKIPCMDDEC ; if at the min skip dec di dec CMD_DEB ; decrement debouncer dec BCMD_DEB decrement debouncer ei SKIPCMDDEC: cp CMD_DEB,#CMD_BREAK ; if not at break then exit jr nz,DECMDEXIT ; if not break then exit call CmdRel ; DECCMDEXIT: ret ; and exit CmdRel: cp L_A_C, #070h ; Test for in learn mode jr nz, NormCmdBreak ; If not, treat normally call SET_STOP_START ; Stop the door NormCmdBreak: di clr CMD_DEB ; reset the debouncer clr BCMD_DEB ; reset the debouncer ei ret DECLIGHT: cp LIGHT_DEB,#00H ; test for the min number jr z,SKIPLIGHTDEC ; if at the min skip dec dec LIGHT_DEB ; decrement debouncer SKIPLIGHTDEC: cp LIGHT_DEB,#LIGHT_BREAK ; if not at break then exit jr nz,DECLIGHTEXIT ; if not break then exit clr LIGHT_DEB ; reset the debouncer DECLIGHTEXIT: ret ; and exit DECVAC: cp VAC_DEB,#00H ; test for one min number jr z,SKIPVACDEC ; if at the min skip dec dec VAC_DEB ; decrement debouncer SKIPVACDEC: cp VACFLAG,#00H ; test for vacation mode jr z,DECVACOUT ; if not vacation use out time DECVACIN: cp VAC_DEB,#VAC_BREAK_IN ; test for the vacation break point jr nz,DECVACEXIT ; exit if not jr CLEARVACDEB ; DECVACOUT: cp VAC_DEB,#VAC_BREAK_OUT ; test for the vacation break point jr nz,DECVACEXIT ; exit if not CLEARVACDEB: clr VAC_DEb ; reset the debouncer DECVACEXIT: ret ; and exit FORCE TABLE force_table: f_0: .byte 000H, 06BH, 06CH .byte 000H, 06BH, 06CH .byte 000H, 06DH, 073H .byte 000H, 06FH, 08EH .byte 000H, 071H, 0BEH .byte 000H, 074H, 004H .byte 000H, 076H, 062H .byte 000H, 078H, 0DAH .byte 000H, 07BH, 06CH .byte 000H, 07EH, 01BH .byte 000H, 080H, 0E8H .byte 000H, 083H, 0D6H .byte 000H, 086H, 09BH .byte 000H, 089H, 07FH .byte 000H, 08CH, 084H .byte 000H, 08FH, 0ABH .byte 000H, 092H, 0F7H .byte 000H, 096H, 06BH .byte 000H, 09AH, 009H .byte 000H, 09DH, 0D5H .byte 000H, 0A1H, 0D2H .byte 000H, 0A6H, 004H .byte 000H, 0AAH, 076H .byte 000H, 0AFH, 027H .byte 000H, 0B4H, 01CH .byte 000H, 0B9H, 05BH .byte 000H, 0BEH, 0EBH .byte 000H, 0C4H, 0D3H .byte 000H, 0CBH, 01BH .byte 000H, 0D1H, 0CDH .byte 000H, 0D8H, 0F4H .byte 000H, 0E0H, 09CH .byte 001H, 005H, 05DH .byte 001H, 00EH, 035H .byte 001H, 017H, 0ABH .byte 001H, 021H, 0D2H .byte 001H, 320H, 0BBH .byte 001H, 138H, 060H .byte 001H, 045H, 03AH .byte 001H, 053H, 008H .byte 001H, 062H, 010H .byte 001H, 072H, 07DH .byte 001H, 084H, 083H .byte 001H, 098H, 061H .byte 001H, 0AEH, 064H .byte 001H, 0C6H, 0E8H .byte 001H, 0E2H, 062H .byte 002H, 001H, 065H .byte 002H, 024H, 0AAH .byte 002H, 04DH, 024H .byte 002H, 07CH, 010H .byte 002H, 0B3H, 01BH .byte 002H, 0F4H, 094H .byte 003H, 043H, 0C1H .byte 003H, 0A5H, 071H .byte 004H, 020H, 0FCH .byte 004H, 0C2H, 038H .byte 005H, 09DH, 080H .byte 013H, 012H, 0D0H f_63: .byte 013H, 012H, 0D0H SIM_TABLE: .WORD 00000H ; Numbers set to zero (proprietary table) .WORD 00000H .WORD 00000H .WORD 00000H .WORD 00000H .WORD 00000H .WORD 00000H .WORD 00000H .WORD 00000H .WORD 00000H .WORD 00000H .WORD 00000H .WORD 00000H .WORD 00000H .WORD 00000H SPEED_TABLE_50: .BYTE 40 .BYTE 34 .BYTE 30 .BYTE 26 .BYTE 27 .BYTE 25 .BYTE 24 .BYTE 23 .BYTE 21 .BYTE 20 .BYTE 17 .BYTE 16 .BYTE 15 .BYTE 13 .BYTE 12 .BYTE 10 .BYTE 8 .BYTE 6 SPEED_TABLE_60: .BYTE 33 .BYTE 29 .BYTE 27 .BYTE 25 .BYTE 23 .BYTE 22 .BYTE 21 .BYTE 20 .BYTE 19 .BYTE 18 .BYTE 17 .BYTE 16 .BYTE 15 .BYTE 13 .BYTE 12 .BYTE 11 .BYTE 10 .BYTE 8 .BYTE 7 .BYTE 5 .BYTE 0 ; Fill 49 bytes of unused memory FILL 10 FILL 10 FILL 10 FILL FILL FILL FILL FILL FILL FILL FILL FILL .end 

What is claimed is:
 1. A movable barrier operator operable from alternating current comprising: an electric motor; a transmission connected to the motor to be driven thereby and to the movable barrier to be moved; an electric circuit for detecting AC line voltage and frequency of the alternating current; a worklight; a first set of operational values for operating the worklight, when a first AC line frequency is detected; a second set of operational values for operating the worklight, when a second AC line frequency is detected; and a controller, responsive to the detected AC line frequency, for activating the corresponding operational set of values for operating the worklight.
 2. A movable barrier operator operable from alternating current according to claim 1 wherein the first AC line frequency comprises 50 Hz and the first set of values comprises a first shut-off time and the second AC line frequency comprises 60 Hz and the second set of values comprises a second shut-off time.
 3. A movable barrier operator operable from alternating current according to claim 2 further comprising a routine for controlling motor speed and wherein the first set of values further comprises a scaling factor for scaling the motor speed.
 4. A movable barrier operator operable from alternating current according to claim 3 wherein the scaling factor is stored in a look-up table stored in a memory.
 5. A movable barrier operator operable from alternating current according to claim 2 wherein the first shut-off time comprises about two and one half minutes and wherein the second shut-off time comprises about four and one half minutes.
 6. A movable barrier operator having linearly variable output speed, comprising: an electric motor having a motor output shaft; a transmission connected to the motor output shaft to be driven thereby and to the movable barrier to be moved; a circuit for providing a pulse signal comprising a series of pulses; a motor control circuit responsive to the pulse signal, for starting the motor and for determining the direction of rotation of the motor output shaft; and a controller for controlling the length of the pulses in the pulse signal in accordance with a predetermined set of values, wherein in accordance with the predetermined set of values, a speed of the motor is linearly varied from zero to a maximum speed and from the maximum speed to zero.
 7. A movable barrier operator according to claim 6 wherein the predetermined set of values causes incrementing of the motor speed from zero to a maximum motor speed in a plurality of steps, causing the motor to operate at the maximum speed for a predetermined period of time, then decrementing the motor speed from the maximum speed to zero in a plurality of steps.
 8. A movable barrier operator according to claim 7 wherein each step comprises a value corresponding to about five percent of a maximum speed of the motor.
 9. A moveable barrier operator according to claim 6 wherein the motor control circuit comprises: a first electromechanical switch for causing the motor output shaft to rotate in a first direction; a second electromechanical switch for causing the motor output shaft to rotate in a second direction; and a solid state device responsive to the pulse signal, for providing current to the motor to cause it to rotate.
 10. A movable barrier operator according to claim 9 wherein the first and second electromechanical switches comprise relays and the solid state device comprises an FET.
 11. A movable barrier operator which automatically detects barrier size, comprising: an electric motor having a maximum output speed; a transmission connected to the motor to be driven thereby and to the movable barrier to be moved; a position detector for sensing the position of the barrier with respect to a frame of reference; and a controller, responsive to the position detector, for calculating a time of travel between a first barrier travel limit and a second barrier travel limit and responsive to the calculated time of barrier travel, for automatically adjusting a barrier travel speed.
 12. A movable barrier operator according to claim 11 wherein the barrier comprises a segmented panel door and wherein the controller adjusts the barrier travel speed such that a maximum barrier travel speed is based on one hundred percent of the motor's maximum output speed.
 13. A movable barrier operator according to claim 11 wherein the barrier comprises a single panel door and wherein the controller adjusts the barrier travel speed such that a maximum barrier travel speed is based on percentage less than one hundred percent of the motor's maximum output speed.
 14. A movable barrier operator according to claim 12 further comprising a routine for varying the motor speed in accordance with a predetermined set of values, wherein in accordance with the predetermined set of values, a speed of the motor is linearly varied from zero to a maximum speed and from the maximum speed to zero.
 15. A movable barrier operator according to claim 13 further comprising a routine for varying the motor speed in accordance with a predetermined set of values, wherein in accordance with the predetermined set of values, a speed of the motor speed is linearly varied from zero to the motor's scaled output speed and from the motor's scaled output speed to zero.
 16. A movable barrier operator having full closure, comprising: an electric motor; a transmission connected to the motor to be driven thereby and connectable to a movable barrier to be moved; a position detector for sensing a position of the barrier; a learn routine for determining a minimum reversal position of the barrier relative to a close limit, wherein the minimum reversal position of the barrier position is located a short distance above the close limit; a controller responsive to the position detector and to a close command to move the barrier to the close limit, for controlling the motor, wherein when the position detector senses the position of the barrier at the minimum reversal position, the controller causes the motor to continue to operate for a predetermined period of time prior to shutting off the motor, effective for driving the barrier to the close limit.
 17. A movable barrier operator according to claim 16 wherein the electric motor comprises a DC motor.
 18. A movable barrier operator according to claim 16 wherein the electric motor comprises an AC motor.
 19. A movable barrier operator according to claim 16 wherein the minimum reversal position is located approximately one inch above the close limit.
 20. A movable barrier operator according to claim 16 wherein the close limit corresponds to a location of a floor.
 21. A movable barrier operator having automatic force settings, comprising: an electric motor; a transmission connected to the motor to be driven thereby and connectable to the movable barrier to be moved; a circuit for providing a pulse signal comprising a series of pulses; a motor control circuit, responsive to the pulse signal, for starting the motor and for determining the direction of rotation of the motor output shaft; a first force command device for setting a first force limit for use when the motor is rotating in a first direction; a second force command device for setting a second force limit for use when the motor is rotating in a second direction; and a controller responsive to the first force limit and to the second force limit for varying the length of the pulses in the pulse signal, effective for varying the motor speed during travel in the first direction and in the second direction.
 22. A movable barrier operator according to claim 21 wherein the barrier comprises a door having a pedestrian door and the operator further comprises a sensor for detecting the position of the pedestrian door, wherein the controller, responsive to the pedestrian door sensor detecting the pedestrian door is not closed, disables movement of the barrier.
 23. A moveable barrier operator according to claim 21 wherein the motor control circuit comprises a first electromechanical switch for causing the motor output shaft to rotate in the first direction, a second electromechanical switch for causing the motor output shaft to rotate in the second direction and a solid state device responsive to the pulse signal, for providing current to the motor to cause it to rotate.
 24. A movable barrier operator according to claim 21 wherein the first force command device comprises a force potentiometer for generating a first analog force signal and the second force command device comprises a force potentiometer for generating a second analog force signal.
 25. A movable barrier operator according to claim 24 further comprising a first A/D converter for converting the first analog signal to a first digital signal and a second A/D converter for converting the second analog signal to a second digital signal.
 26. A movable barrier operator according to claim 25 further comprising a look-up table comprising a plurality of motor speeds stored in a memory in the controller, wherein responsive to the first digital signal and the second digital signal selects a corresponding motor speed stored in the look-up table.
 27. A movable barrier operator having a flasher module, comprising: an electric motor; a transmission connected to the motor to be driven thereby and connectable to a movable barrier to be moved; a flasher module light; a flasher routine for enabling and disabling the flasher module light in a predetermined pattern; a controller, responsive to a command to move the barrier, for controlling the motor and for automatically detecting the presence of the flasher module light, wherein responsive only to the presence of the flasher module light, the controller executes the flasher routine and delays starting the motor for a predetermined delay time.
 28. A movable barrier operator according to claim 27 , wherein the flasher routine continues until the controller causes the motor to stop.
 29. A movable barrier operator according to claim 27 wherein the predetermined delay time comprises about two seconds.
 30. A movable barrier operator according to claim 27 , wherein the flasher routine continues only during the predetermined delay period. 